## Automatically generated incremental diff
## From:   linux-2.4.23-pre5
## To:     linux-2.4.23-pre6
## Robot:  $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $

diff -urN linux-2.4.23-pre5/Documentation/Configure.help linux-2.4.23-pre6/Documentation/Configure.help
--- linux-2.4.23-pre5/Documentation/Configure.help	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/Documentation/Configure.help	2003-10-01 10:38:45.000000000 -0700
@@ -4006,7 +4006,7 @@
 
 CONFIG_AGP_AMD_8151
   This option gives you AGP support for the GLX component of
-  XFree86 on AMD K8 with an AGP 8151 chipset.
+  XFree86 on an AMD Opteron/Athlon64 using the on-CPU GART.
 
   You should say Y here if you use XFree86 3.3.6 or 4.x and want to
   use GLX or DRI.  If unsure, say N.
@@ -6108,6 +6108,61 @@
 
   It is safe to say N here for now.
 
+The SCTP Protocol (EXPERIMENTAL)
+CONFIG_IP_SCTP
+  Stream Control Transmission Protocol
+
+  From RFC 2960 (http://www.ietf.org/rfc/rfc2960.txt)
+
+  "SCTP is a reliable transport protocol operating on top of a
+  connectionless packet network such as IP.  It offers the following
+  services to its users:
+
+  -- acknowledged error-free non-duplicated transfer of user data,
+  -- data fragmentation to conform to discovered path MTU size,
+  -- sequenced delivery of user messages within multiple streams,
+  with an option for order-of-arrival delivery of individual user
+  messages,
+  -- optional bundling of multiple user messages into a single SCTP
+  packet, and
+  -- network-level fault tolerance through supporting of multi-
+  homing at either or both ends of an association."
+
+  This protocol support is also available as a module ( = code which
+  can be inserted in and removed from the running kernel whenever you
+  want). The module will be called sctp. If you want to compile it
+  as a module, say M here and read <file:Documentation/modules.txt>.
+
+  If in doubt, say N.
+
+SCTP: Use old checksum (Adler-32)
+CONFIG_SCTP_ADLER32
+  RCF2960 currently specifies the Adler-32 checksum algorithm for SCTP.
+  This has been deprecated and replaced by an algorithm now referred
+  to as crc32c.
+
+  If you say Y, this will use the Adler-32 algorithm, this might be 
+  useful for interoperation with downlevel peers. 
+
+  If unsure, say N.  
+
+SCTP: Debug messages
+CONFIG_SCTP_DBG_MSG
+  If you say Y, this will enable verbose debugging messages. 
+
+  If unsure, say N.  However, if you are running into problems, use 
+  this option to gather detailed trace information
+
+SCTP: Debug object counts
+CONFIG_SCTP_DBG_OBJCNT
+  If you say Y, this will enable debugging support for counting the 
+  type of objects that are currently allocated.  This is useful for 
+  identifying memory leaks.   If the /proc filesystem is enabled this 
+  debug information can be viewed by 
+  'cat /proc/net/sctp/sctp_dbg_objcnt'
+
+  If unsure, say N
+
 Kernel httpd acceleration
 CONFIG_KHTTPD
   The kernel httpd acceleration daemon (kHTTPd) is a (limited) web
@@ -11591,49 +11646,58 @@
   Say Y here if you have a Marvell Yukon or SysKonnect SK-98xx/SK-95xx
   compliant Gigabit Ethernet Adapter. The following adapters are supported
   by this driver:
-  - 3Com 3C940 Gigabit LOM Ethernet Adapter
-  - 3Com 3C941 Gigabit LOM Ethernet Adapter
-  - SK-9821 Gigabit Ethernet Server Adapter (SK-NET GE-T)
-  - SK-9822 Gigabit Ethernet Server Adapter (SK-NET GE-T dual link)
-  - SK-9841 Gigabit Ethernet Server Adapter (SK-NET GE-LX)
-  - SK-9842 Gigabit Ethernet Server Adapter (SK-NET GE-LX dual link)
-  - SK-9843 Gigabit Ethernet Server Adapter (SK-NET GE-SX)
-  - SK-9844 Gigabit Ethernet Server Adapter (SK-NET GE-SX dual link)
-  - SK-9861 Gigabit Ethernet Server Adapter (SK-NET GE-SX Volition)
-  - SK-9862 Gigabit Ethernet Server Adapter (SK-NET GE-SX Volition dual link)
-  - SK-9871 Gigabit Ethernet Server Adapter (SK-NET GE-ZX)
-  - SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link)
-  - Allied Telesyn AT-2970SX Gigabit Ethernet Adapter
-  - Allied Telesyn AT-2970LX Gigabit Ethernet Adapter
-  - Allied Telesyn AT-2970TX Gigabit Ethernet Adapter
-  - Allied Telesyn AT-2971SX Gigabit Ethernet Adapter
-  - Allied Telesyn AT-2971T Gigabit Ethernet Adapter
-  - Allied Telesyn AT-2970SX/2SC Gigabit Ethernet Adapter
-  - Allied Telesyn AT-2970LX/2SC Gigabit Ethernet Adapter
-  - Allied Telesyn AT-2970TX/2TX Gigabit Ethernet Adapter
-  - Marvell RDK-8001 Adapter
-  - Marvell RDK-8002 Adapter
-  - Marvell RDK-8003 Adapter
-  - Marvell RDK-8004 Adapter
-  - Marvell RDK-8006 Adapter
-  - Marvell RDK-8007 Adapter
-  - Marvell RDK-8008 Adapter
-  - Marvell RDK-8009 Adapter
-  - Marvell RDK-8011 Adapter
-  - Marvell RDK-8012 Adapter
-  - SK-9521 V2.0 10/100/1000Base-T Adapter
-  - SK-9821 V2.0 Gigabit Ethernet 10/100/1000Base-T Adapter
-  - SK-9841 V2.0 Gigabit Ethernet 1000Base-LX Adapter
-  - SK-9843 V2.0 Gigabit Ethernet 1000Base-SX Adapter
-  - SK-9851 V2.0 Gigabit Ethernet 1000Base-SX Adapter
-  - SK-9861 V2.0 Gigabit Ethernet 1000Base-SX Adapter
-  - SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter
-  - SK-9521 10/100/1000Base-T Adapter
-  - DGE-530T Gigabit Ethernet Adapter
-  - Marvell Yukon Gigabit Ethernet 10/100/1000Base-T Adapter
-  - N-Way PCI-Bus Giga-Card 1000/100/10Mbps(L)
-  - EG1032 v2 Instant Gigabit Network Adapter
-  - EG1064 v2 Instant Gigabit Network Adapter
+	    - 3Com 3C940 Gigabit LOM Ethernet Adapter
+	    - 3Com 3C941 Gigabit LOM Ethernet Adapter
+	    - Allied Telesyn AT-2970LX Gigabit Ethernet Adapter
+	    - Allied Telesyn AT-2970LX/2SC Gigabit Ethernet Adapter
+	    - Allied Telesyn AT-2970SX Gigabit Ethernet Adapter
+	    - Allied Telesyn AT-2970SX/2SC Gigabit Ethernet Adapter
+	    - Allied Telesyn AT-2970TX Gigabit Ethernet Adapter
+	    - Allied Telesyn AT-2970TX/2TX Gigabit Ethernet Adapter
+	    - Allied Telesyn AT-2971SX Gigabit Ethernet Adapter
+	    - Allied Telesyn AT-2971T Gigabit Ethernet Adapter
+	    - DGE-530T Gigabit Ethernet Adapter
+	    - EG1032 v2 Instant Gigabit Network Adapter
+	    - EG1064 v2 Instant Gigabit Network Adapter
+	    - Marvell 88E8001 Gigabit LOM Ethernet Adapter (Abit)
+	    - Marvell 88E8001 Gigabit LOM Ethernet Adapter (Asus)
+	    - Marvell 88E8001 Gigabit LOM Ethernet Adapter (ECS)
+	    - Marvell 88E8001 Gigabit LOM Ethernet Adapter (Epox)
+	    - Marvell 88E8001 Gigabit LOM Ethernet Adapter (Gigabyte)
+	    - Marvell 88E8001 Gigabit LOM Ethernet Adapter (Iwill)
+	    - Marvell RDK-8001 Adapter
+	    - Marvell RDK-8002 Adapter
+	    - Marvell RDK-8003 Adapter
+	    - Marvell RDK-8004 Adapter
+	    - Marvell RDK-8006 Adapter
+	    - Marvell RDK-8007 Adapter
+	    - Marvell RDK-8008 Adapter
+	    - Marvell RDK-8009 Adapter
+	    - Marvell RDK-8010 Adapter
+	    - Marvell RDK-8011 Adapter
+	    - Marvell RDK-8012 Adapter
+	    - Marvell Yukon Gigabit Ethernet 10/100/1000Base-T Adapter (32 bit)
+	    - Marvell Yukon Gigabit Ethernet 10/100/1000Base-T Adapter (64 bit)
+	    - N-Way PCI-Bus Giga-Card 1000/100/10Mbps(L)
+	    - SK-9521 10/100/1000Base-T Adapter
+	    - SK-9521 V2.0 10/100/1000Base-T Adapter
+	    - SK-9821 Gigabit Ethernet Server Adapter (SK-NET GE-T)
+	    - SK-9821 V2.0 Gigabit Ethernet 10/100/1000Base-T Adapter
+	    - SK-9822 Gigabit Ethernet Server Adapter (SK-NET GE-T dual link)
+	    - SK-9841 Gigabit Ethernet Server Adapter (SK-NET GE-LX)
+	    - SK-9841 V2.0 Gigabit Ethernet 1000Base-LX Adapter
+	    - SK-9842 Gigabit Ethernet Server Adapter (SK-NET GE-LX dual link)
+	    - SK-9843 Gigabit Ethernet Server Adapter (SK-NET GE-SX)
+	    - SK-9843 V2.0 Gigabit Ethernet 1000Base-SX Adapter
+	    - SK-9844 Gigabit Ethernet Server Adapter (SK-NET GE-SX dual link)
+	    - SK-9851 V2.0 Gigabit Ethernet 1000Base-SX Adapter
+	    - SK-9861 Gigabit Ethernet Server Adapter (SK-NET GE-SX Volition)
+	    - SK-9861 V2.0 Gigabit Ethernet 1000Base-SX Adapter
+	    - SK-9862 Gigabit Ethernet Server Adapter (SK-NET GE-SX Volition dual link)
+	    - SK-9871 Gigabit Ethernet Server Adapter (SK-NET GE-ZX)
+	    - SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter
+	    - SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link)
+	    - SMC EZ Card 1000 (SMC9452TXV.2)
 
   The adapters support Jumbo Frames.
   The dual link adapters support link-failover and dual port features.
@@ -11649,121 +11713,6 @@
   say M here and read Documentation/modules.txt. This is recommended.
   The module will be called sk98lin.o.
 
-Marvell Yukon Chipset
-CONFIG_SK98LIN_T1
-  This driver supports:
-
-  - 3Com 3C940 Gigabit LOM Ethernet Adapter
-  - 3Com 3C941 Gigabit LOM Ethernet Adapter
-
-  Questions concerning this driver may be addressed to:
-    linux@syskonnect.de
-
-SysKonnect SK98xx Support
-CONFIG_SK98LIN_T2
-  This driver supports:
-
-  - SK-9821 Gigabit Ethernet Server Adapter (SK-NET GE-T)
-  - SK-9822 Gigabit Ethernet Server Adapter (SK-NET GE-T dual link)
-  - SK-9841 Gigabit Ethernet Server Adapter (SK-NET GE-LX)
-  - SK-9842 Gigabit Ethernet Server Adapter (SK-NET GE-LX dual link)
-  - SK-9843 Gigabit Ethernet Server Adapter (SK-NET GE-SX)
-  - SK-9844 Gigabit Ethernet Server Adapter (SK-NET GE-SX dual link)
-  - SK-9861 Gigabit Ethernet Server Adapter (SK-NET GE-SX Volition)
-  - SK-9862 Gigabit Ethernet Server Adapter (SK-NET GE-SX Volition dual link)
-  - SK-9871 Gigabit Ethernet Server Adapter (SK-NET GE-ZX)
-  - SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link)
-
-  Questions concerning this driver may be addressed to:
-    linux@syskonnect.de
-
-SysKonnect SK98xx Support
-CONFIG_SK98LIN_T3
-  This driver supports:
-
-  - Allied Telesyn AT-2970SX Gigabit Ethernet Adapter
-  - Allied Telesyn AT-2970LX Gigabit Ethernet Adapter
-  - Allied Telesyn AT-2970TX Gigabit Ethernet Adapter
-  - Allied Telesyn AT-2971SX Gigabit Ethernet Adapter
-  - Allied Telesyn AT-2971T Gigabit Ethernet Adapter
-  - Allied Telesyn AT-2970SX/2SC Gigabit Ethernet Adapter
-  - Allied Telesyn AT-2970LX/2SC Gigabit Ethernet Adapter
-  - Allied Telesyn AT-2970TX/2TX Gigabit Ethernet Adapter
-
-  Questions concerning this driver may be addressed to:
-    linux@syskonnect.de
-
-Marvell Yukon Chipset
-CONFIG_SK98LIN_T4
-  This driver supports:
-
-  - Marvell RDK-8001 Adapter
-  - Marvell RDK-8002 Adapter
-  - Marvell RDK-8003 Adapter
-  - Marvell RDK-8004 Adapter
-  - Marvell RDK-8006 Adapter
-  - Marvell RDK-8007 Adapter
-  - Marvell RDK-8008 Adapter
-  - Marvell RDK-8009 Adapter
-  - Marvell RDK-8011 Adapter
-  - Marvell RDK-8012 Adapter
-
-  Questions concerning this driver may be addressed to:
-    linux@syskonnect.de
-
-Marvell Yukon Chipset
-CONFIG_SK98LIN_T5
-  This driver supports:
-
-  - SK-9521 V2.0 10/100/1000Base-T Adapter
-  - SK-9821 V2.0 Gigabit Ethernet 10/100/1000Base-T Adapter
-  - SK-9841 V2.0 Gigabit Ethernet 1000Base-LX Adapter
-  - SK-9843 V2.0 Gigabit Ethernet 1000Base-SX Adapter
-  - SK-9851 V2.0 Gigabit Ethernet 1000Base-SX Adapter
-  - SK-9861 V2.0 Gigabit Ethernet 1000Base-SX Adapter
-  - SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter
-  - SK-9521 10/100/1000Base-T Adapter
-
-  Questions concerning this driver may be addressed to:
-    linux@syskonnect.de
-
-Marvell Yukon Chipset
-CONFIG_SK98LIN_T6
-  This driver supports:
-
-  - DGE-530T Gigabit Ethernet Adapter
-
-  Questions concerning this driver may be addressed to:
-    linux@syskonnect.de
-
-Marvell Yukon Chipset
-CONFIG_SK98LIN_T7
-  This driver supports:
-
-  - Marvell Yukon Gigabit Ethernet 10/100/1000Base-T Adapter
-
-  Questions concerning this driver may be addressed to:
-    linux@syskonnect.de
-
-Marvell Yukon Chipset
-CONFIG_SK98LIN_T8
-  This driver supports:
-
-  - N-Way PCI-Bus Giga-Card 1000/100/10Mbps(L)
-
-  Questions concerning this driver may be addressed to:
-    linux@syskonnect.de
-
-Marvell Yukon Chipset
-CONFIG_SK98LIN_T9
-  This driver supports:
-
-  - EG1032 v2 Instant Gigabit Network Adapter
-  - EG1064 v2 Instant Gigabit Network Adapter
-
-  Questions concerning this driver may be addressed to:
-    linux@syskonnect.de
-
 
 Sun GEM support
 CONFIG_SUNGEM
@@ -19705,6 +19654,9 @@
   display brightness and output, switching the LCD backlight on and off,
   and most importantly, allows you to blink those fancy LEDs intended
   for reporting mail and wireless status.
+
+  Note: the display switching code is currently considered EXPERIMENTAL,
+  toying with these values may even lock your machine.
   
   All settings are changed via /proc/acpi/asus directory entries. Owner
   and group for these entries can be set with asus_uid and asus_gid
diff -urN linux-2.4.23-pre5/Documentation/cciss.txt linux-2.4.23-pre6/Documentation/cciss.txt
--- linux-2.4.23-pre5/Documentation/cciss.txt	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/Documentation/cciss.txt	2003-10-01 10:38:45.000000000 -0700
@@ -53,6 +53,56 @@
 /dev/cciss/c1d1p2		Controller 1, disk 1, partition 2
 /dev/cciss/c1d1p3		Controller 1, disk 1, partition 3
 
+Support for more than 8 controllers
+-----------------------------------
+Originally the driver only supports 8 controllers in the system,
+and this is due to the major numbers assigned to the driver
+(104 thru 111).
+
+The driver can now support up to 32 controllers in the system.
+
+For the ninth controller and beyond, the major numbers will be
+assigned dynamically by the kernel when it is discovered,
+and there is no guarantee what the major number you will get,
+but most likely it will start from 254 and goes down from there.
+
+You can check the assigned major numbers by typing
+	cat /proc/devices
+And look for cciss controllers
+
+Once you have this, you need to create device nodes in
+/dev/cciss directory. The nodes for the first 8 controllers
+should already be created by mkdev.cciss script or
+/etc/makedev.d/cciss script. You can add the major number(s)
+in those scripts, or create the nodes manually by using
+the mknod command.
+
+You can also use mknod_dyn.cciss and rmnod_dyn.cciss scripts
+to create or remove nodes easily. These scripts can be found
+in the Documentation directory.
+
+Then you can mount the devices and create partitions
+(You also need to make nodes for these partitions).
+
+As for the minor number, the disk device will have a minor
+number divisible by 16 (e.g: 0, 16, 32 etc), and the 
+partitions on those disk devices will have the minor number
+of the disk device plus the partition number (1-15).
+For example, disk d2 will have minor number 32, and its
+partitions 1 and 2 will have minor numbers 33 and 34.
+
+Look at the mkdev.cciss script for example.
+ 
+Note:
+ In 2.4 kernel, partition names are hard coded in
+ /usr/src/linux/fs/partitions/check.c
+ and only for the first 8 cciss controllers. The rest
+ will be reported as ccissXX. This should not affect 
+ I/O operation or performance. Please apply the 
+ cciss_2.4_partition_name.patch to address this. This 
+ will not be an issue under 2.5 kernel, since partition
+ names will be handled by the driver.
+
 SCSI tape drive and medium changer support
 ------------------------------------------
 
diff -urN linux-2.4.23-pre5/Documentation/mkdev_dyn.cciss linux-2.4.23-pre6/Documentation/mkdev_dyn.cciss
--- linux-2.4.23-pre5/Documentation/mkdev_dyn.cciss	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/Documentation/mkdev_dyn.cciss	2003-10-01 10:38:45.000000000 -0700
@@ -0,0 +1,171 @@
+#!/bin/sh
+#
+# Author: Francis Wiran <Francis.Wiran@hp.com>
+#
+# Script to create device nodes for SMART Array controllers, idea from
+# mkdev.cciss found in Documentation. The argument syntax is different,
+# hence, the name change.
+#
+# Usage:
+# 	mkdev_dyn.cciss [ctlr num] [major num] [num log vol] [num partitions]
+#
+# With no arguments, the script will check to see if there are any cciss
+# nodes already created in the /dev directory. If so, then it will assume
+# that the nodes for the first 8 controllers are already created by
+# /etc/makedev.d, which is likely. If not, then the script will create 
+# them, using the major numbers reserved for cciss controllers (104 thru 111).
+#
+# After that, it will start creating nodes for the controllers which major
+# numbers are dynamically allocated by the kernel. It will check 
+# /proc/devices for any cciss controllers with major numbers other than
+# 104 thru 111 and creates the nodes.
+#
+# Note that it is a good idea to run rmdev_dyn.cciss script if you remove
+# those controllers (the ones which major numbers were dynamically allocated)
+# This will unclutter /dev, as well as preventing possible problems due to 
+# referenced devices and major numbers no longer available or taken by
+# other non-cciss drivers.
+#
+# With no arguments, the script assumes 16 logical volumes per controller
+# and 16 partitions per volume; 
+#
+# Passing arguments:
+# If you know that one of your controllers, say cciss8, has been dynamically 
+# assigned major number 254, and were planning on no more than 2 logical 
+# volumes, using a maximum of 4 partitions per volume, you could do:
+#
+# mkdev_dyn.cciss 8 254 2 4
+#
+# Of course, this has no real benefit over "mkdev_dyn.cciss 8 254" except 
+# that it doesn't create as many device nodes in /dev/cciss.
+#
+
+# Inputs
+NR_CTLR=${1-8}
+NR_MAJOR=${2-254}
+NR_VOL=${3-16}
+NR_PART=${4-16}
+
+echo_usage()
+{
+	echo "Usage: mkdev_dyn.cciss [ctlr num] [maj] [volumes] [partitions]"
+	echo "The script assumes that ctlr 0 thru 7 is statically created"
+	echo "Therefore, if you want to pass arguments make sure that"
+	echo "ctlr num is equal or greater than 8, and the major number"
+	echo "is not one that's statically assigned for cciss controller."
+	echo "Check the correct major number in /proc/devices"
+
+	# Hmm... we don't support volumes and partitions greater than 16
+	# either.
+}
+
+echo_create()
+{
+	echo "created /dev/cciss/c${1}* nodes using major number ${2}"
+}
+
+
+# Function: do_mknod()
+#           Creates devices nodes under /dev/cciss
+# Inputs: $1 - ctlr number
+#	  $2 - major number
+#	  $3 - number of devices on controller
+#	  $4 - number of partitions per device
+do_mknod()
+{
+	D=0;
+	while [ $D -lt $3 ]; do
+		P=0;
+		while [ $P -lt $4 ]; do
+			MIN=`expr $D \* 16 + $P`;
+			if [ $P -eq 0 ]; then
+				mknod /dev/cciss/c${1}d${D} b $2 $MIN
+			else
+				mknod /dev/cciss/c${1}d${D}p${P} b $2 $MIN
+			fi
+			P=`expr $P + 1`;
+		done
+		D=`expr $D + 1`;
+	done
+}
+
+# Function: do_dyn
+#           Search and create cciss nodes for the controllers which
+#           major numbers are allocated dynamically by the kernel
+#           instead of using kernel.org 's value of 104 to 111.
+#
+# Input: $1 - ctlr num - will create nodes /dev/cciss/c${1}
+#	 $2 - major num - the major number to assign to the nodes
+#	 $3 - volumes - max number of volumes per controller
+#	 $4 - partitions - max number of partitions per volume
+#
+# Note: Without input, this function will start creating nodes
+#       for controller c8 and above, making assumption that 
+#       c0 thru c7 are already made, and the name c8 and above 
+#       are not already taken.
+do_dyn()
+{
+	if [ $# -eq 0 ]; then
+		C=0;
+		for MAJ in `cat /proc/devices |\
+			grep cciss |\
+			awk '/cciss[0-9]$/ { sub("cciss", "cciss0") }; {print}' |\
+			sort -k 2,2 |\
+			awk '{print $1-i}'`;
+		do
+			if [ `ls -l /dev/cciss/c* |\
+				awk '{print $5-i}' |\
+				uniq |\
+				grep $MAJ |\
+				wc -l` -gt 0 ]; then
+				:
+			else
+				do_mknod $C $MAJ $NR_VOL $NR_PART;
+				echo_create $C $MAJ;
+			fi
+			C=`expr $C + 1`;
+		done
+	else
+		do_mknod $1 $2 $3 $4;
+		echo_create $1 $2;
+	fi
+	
+	exit
+}
+
+# Start here
+
+# Check the input values
+if [ $NR_CTLR -lt 8 ]; then
+	echo_usage;
+	exit
+fi
+
+if [ $NR_CTLR -ge 8 ]; then
+	if [ \( $NR_MAJOR -ge 104 \) -a \( $NR_MAJOR -le 111 \) ]; then
+		echo_usage;
+		exit
+	fi
+fi
+
+if [ ! -d /dev/cciss ]; then
+        mkdir -p /dev/cciss
+fi
+
+# Assume that if c0d0p1 node already exist, then all nodes from c0d0p1
+# to c7d15p15 have been created for us.
+if [ ! -b /dev/cciss/c0d0p1 ]; then
+	C=0; 
+	while [ $C -lt 8 ]; do
+		MAJ=`expr $C + 104`;
+		do_mknod $C $MAJ $NR_VOL $NR_PART;
+		echo_create $C $MAJ;
+		C=`expr $C + 1`;
+	done
+fi
+
+if [ $# -gt 0 ]; then
+	do_dyn $NR_CTLR $NR_MAJOR $NR_VOL $NR_PART;
+else
+	do_dyn;
+fi
diff -urN linux-2.4.23-pre5/Documentation/networking/atm.txt linux-2.4.23-pre6/Documentation/networking/atm.txt
--- linux-2.4.23-pre5/Documentation/networking/atm.txt	2000-02-08 18:23:13.000000000 -0800
+++ linux-2.4.23-pre6/Documentation/networking/atm.txt	2003-10-01 10:38:45.000000000 -0700
@@ -1,8 +1,8 @@
 In order to use anything but the most primitive functions of ATM,
 several user-mode programs are required to assist the kernel. These
 programs and related material can be found via the ATM on Linux Web
-page at http://icawww1.epfl.ch/linux-atm/
+page at http://linux-atm.sourceforge.net/
 
 If you encounter problems with ATM, please report them on the ATM
 on Linux mailing list. Subscription information, archives, etc.,
-can be found on http://icawww1.epfl.ch/linux-atm/
+can be found on http://linux-atm.sourceforge.net/
diff -urN linux-2.4.23-pre5/Documentation/networking/sk98lin.txt linux-2.4.23-pre6/Documentation/networking/sk98lin.txt
--- linux-2.4.23-pre5/Documentation/networking/sk98lin.txt	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/Documentation/networking/sk98lin.txt	2003-10-01 10:38:45.000000000 -0700
@@ -2,9 +2,9 @@
 All rights reserved
 ===========================================================================
 
-sk98lin.txt created 26-Aug-2003
+sk98lin.txt created 23-Sep-2003
 
-Readme File for sk98lin v6.17
+Readme File for sk98lin v6.18
 Marvell Yukon/SysKonnect SK-98xx Gigabit Ethernet Adapter family driver for LINUX
 
 This file contains
diff -urN linux-2.4.23-pre5/Documentation/rmdev_dyn.cciss linux-2.4.23-pre6/Documentation/rmdev_dyn.cciss
--- linux-2.4.23-pre5/Documentation/rmdev_dyn.cciss	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/Documentation/rmdev_dyn.cciss	2003-10-01 10:38:45.000000000 -0700
@@ -0,0 +1,87 @@
+#!/bin/sh 
+#
+# Author: Francis Wiran <Francis.Wiran@hp.com>
+#
+# Script to remove device nodes for SMART Array controllers. This will
+# be the device nodes with major numbers which are dynamically allocated
+# by the kernel. This script will not attempt to remove the device
+# nodes with major number 104 thru 111 (c0 thru c7), which are 
+# the major numbers that's allocated for cciss controllers by kernel.org.
+#
+# Usage:
+# rmdev_dyn.cciss [ctlr num]
+#
+# With no arguments, the script will check to see if there are any nodes
+# under /dev/cciss, whose major number no longer shows in /proc/partitions,
+# or to be exact, no longer shows to be owned by cciss driver.
+# If there is, then it will be removed.
+#
+# Note that it is a good idea to run rmdev_dyn.cciss script if you remove
+# those controllers (the ones which major numbers were dynamically allocated)
+# This will unclutter /dev, as well as preventing possible problems due to
+# referenced devices and major numbers no longer available or taken by
+# other non-cciss drivers.
+#
+# Passing arguments:
+# If you know that one of your controllers, say cciss8, has been removed
+# and the nodes are no longer valid, you could do
+#
+# rmdev_dyn.cciss 8 
+#
+# This is the same as doing `rm -f /dev/cciss/c8*` 
+
+# Inputs
+NR_CTLR=${1}
+
+echo_usage()
+{
+	echo "Usage: rmdev_dyn.cciss [ctlr num]"
+	echo "The script will not attempt to remove nodes for controllers"
+	echo "0 thru 7, therefore if you want to pass an argument,"
+	echo "make sure that ctlr num is equal or greater than 8"
+}
+
+rm_nod1()
+{
+	if [ $1 -lt 8 ]; then
+		echo_usage;
+		exit
+	else
+		rm -f /dev/cciss/c${1}*
+		echo "removed /dev/cciss/c${1}*"
+	fi
+}
+
+rm_nod2()
+{
+	for X in `ls -l /dev/cciss/c* |\
+		awk '{print $5-i}' |\
+		uniq`; do
+		if [ \( $X -ge 104 \) -a \( $X -le 111 \) ]; then
+			:
+		elif [ `cat /proc/devices |\
+			grep cciss |\
+			grep $X |\
+			wc -l` -eq 0 ]; then
+
+			Y=`ls -l /dev/cciss/ |\
+				awk '{print $5-i ":"  $10}'|\
+				tr d ':' |\
+				grep $X |\
+				awk -F: '{print $2}' |\
+				uniq`
+
+			Z="/dev/cciss/${Y}*"
+
+			rm -f $Z
+			echo "removed $Z"
+		fi
+	done
+}
+
+# Start here
+if [ $# -gt 0 ]; then
+	rm_nod1 $NR_CTLR;
+else
+	rm_nod2;
+fi
diff -urN linux-2.4.23-pre5/Documentation/x86_64/boot-options.txt linux-2.4.23-pre6/Documentation/x86_64/boot-options.txt
--- linux-2.4.23-pre5/Documentation/x86_64/boot-options.txt	2003-08-25 04:44:39.000000000 -0700
+++ linux-2.4.23-pre6/Documentation/x86_64/boot-options.txt	2003-10-01 10:38:45.000000000 -0700
@@ -65,13 +65,14 @@
   0 don't use an NMI watchdog
   1 use the IO-APIC timer for the NMI watchdog
   2 use the local APIC for the NMI watchdog using a performance counter. Note
-  This will use one performance counter.
+  This will use one performance counter and the local APIC's performance
+  counter vector.
 
 Idle loop
 
   idle=poll
   Don't do power saving in the idle loop using HLT, but poll for rescheduling 
-  event. This will make the CPUs eat a lot more power, but may be useful 
+  events. This will make the CPUs burn a lot more power, but may be useful 
   to get slightly better performance in multiprocessor benchmarks. It also
   makes some profiling using performance counters more accurate.
 
@@ -145,7 +146,7 @@
    off   don't use the IOMMU
    leak  turn on simple iommu leak tracing (only when CONFIG_IOMMU_LEAK is on)
    memaper[=order] allocate an own aperture over RAM with size 32MB^order.
-   noforce don't force IOMMU usage. Should be fastest.
-   force  Force IOMMU and turn on unmap debugging.  
+   noforce don't force IOMMU usage. Default.
+   force  Force IOMMU for all devices.
 
 
diff -urN linux-2.4.23-pre5/MAINTAINERS linux-2.4.23-pre6/MAINTAINERS
--- linux-2.4.23-pre5/MAINTAINERS	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/MAINTAINERS	2003-10-01 10:38:57.000000000 -0700
@@ -266,6 +266,16 @@
 L:	linux-net@vger.kernel.org
 S:	Maintained
 
+ASUS ACPI EXTRAS DRIVER
+P:	Karol Kozimor
+M:	sziwan@users.sourceforge.net
+P:	Julien Lerouge
+M:	julien.lerouge@free.fr
+L:	acpi4asus-user@lists.sourceforge.net
+W:	http://sourceforge.net/projects/acpi4asus
+W:	http://julien.lerouge.free.fr
+S:	Maintained
+
 ATM
 P:	Chas Williams
 M:	chas@cmf.nrl.navy.mil
diff -urN linux-2.4.23-pre5/Makefile linux-2.4.23-pre6/Makefile
--- linux-2.4.23-pre5/Makefile	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/Makefile	2003-10-01 10:38:57.000000000 -0700
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 23
-EXTRAVERSION = -pre5
+EXTRAVERSION = -pre6
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
diff -urN linux-2.4.23-pre5/arch/alpha/Makefile linux-2.4.23-pre6/arch/alpha/Makefile
--- linux-2.4.23-pre5/arch/alpha/Makefile	2002-08-02 17:39:42.000000000 -0700
+++ linux-2.4.23-pre6/arch/alpha/Makefile	2003-10-01 10:38:44.000000000 -0700
@@ -131,3 +131,6 @@
 
 bootpfile:
 	@$(MAKEBOOT) bootpfile
+
+bootpzfile:
+	@$(MAKEBOOT) bootpzfile
diff -urN linux-2.4.23-pre5/arch/alpha/boot/Makefile linux-2.4.23-pre6/arch/alpha/boot/Makefile
--- linux-2.4.23-pre5/arch/alpha/boot/Makefile	2001-10-04 18:47:08.000000000 -0700
+++ linux-2.4.23-pre6/arch/alpha/boot/Makefile	2003-10-01 10:38:44.000000000 -0700
@@ -7,6 +7,21 @@
 #
 # Copyright (C) 1994 by Linus Torvalds
 #
+# Creating a compressed kernel image (arch/alpha/boot/vmlinux.gz)
+# is done via:
+#
+#	make boot
+#
+# Creating a BOOTP image (arch/alpha/boot/bootpfile or bootpzfile)
+# is done via:
+#
+#	make bootpfile		# using an uncompressed kernel
+#	make bootpzfile		# using a compressed kernel
+#
+# Adding an INITRD image to a BOOTP file (arch/alpha/boot/bootpzfile)
+# can be done, e.g, via:
+#
+# INITRD=`pwd`/initrd.img make bootpzfile
 
 LINKFLAGS = -static -T bootloader.lds #-N -relax
 
@@ -17,6 +32,7 @@
 
 OBJECTS = head.o main.o
 BPOBJECTS = head.o bootp.o
+BPZOBJECTS = head.o bootpz.o misc.o
 TARGETS = vmlinux.gz tools/objstrip # also needed by aboot & milo
 VMLINUX = $(TOPDIR)/vmlinux
 OBJSTRIP = tools/objstrip
@@ -41,6 +57,12 @@
 	cat $(INITRD) >> bootpfile
 endif
 
+bootpzfile:	tools/bootpzh vmlinux.nh.gz
+	cat tools/bootpzh vmlinux.nh.gz > bootpzfile
+ifdef INITRD
+	cat $(INITRD) >> bootpzfile
+endif
+
 srmboot:	bootdevice bootimage
 	dd if=bootimage of=$(BOOTDEV) bs=512 seek=1 skip=1
 	tools/mkbb $(BOOTDEV) tools/lxboot
@@ -48,16 +70,31 @@
 bootdevice:
 	@test "$(BOOTDEV)" != ""  || (echo You must specify BOOTDEV ; exit -1)
 
-vmlinux.gz: vmlinux
-	gzip -fv9 vmlinux
+vmlinux.gz: $(TOPDIR)/vmlinux
+	gzip -fv9c $(TOPDIR)/vmlinux > vmlinux.gz
+
+vmlinux.nh.gz: vmlinux.nh
+	gzip -fv9c vmlinux.nh > vmlinux.nh.gz
 
 main.o: ksize.h
 
 bootp.o: ksize.h
 
+bootpz.o: kzsize.h
+
 ksize.h: vmlinux.nh dummy
 	echo "#define KERNEL_SIZE `ls -l vmlinux.nh | awk '{print $$5}'`" > $@T
 ifdef INITRD
+	[ ! \( -f $(INITRD) \) ] || exit 1
+	echo "#define INITRD_IMAGE_SIZE `ls -l $(INITRD) | awk '{print $$5}'`" >> $@T
+endif
+	cmp -s $@T $@ || mv -f $@T $@
+	rm -f $@T
+
+kzsize.h: vmlinux.nh.gz dummy
+	echo "#define KERNEL_SIZE `ls -l vmlinux.nh | awk '{print $$5}'`" > $@T
+	echo "#define KERNEL_Z_SIZE `ls -l vmlinux.nh.gz | awk '{print $$5}'`" >> $@T
+ifdef INITRD
 	[ -f $(INITRD) ] || exit 1
 	echo "#define INITRD_IMAGE_SIZE `ls -l $(INITRD) | awk '{print $$5}'`" >> $@T
 endif
@@ -67,7 +104,7 @@
 vmlinux.nh: $(VMLINUX) $(OBJSTRIP)
 	$(OBJSTRIP) -v $(VMLINUX) vmlinux.nh
 
-vmlinux: $(TOPDIR)/vmlinux
+vmlinux: $(VMLINUX)
 	$(STRIP) -o vmlinux $(VMLINUX)
 
 tools/lxboot: $(OBJSTRIP) bootloader
@@ -79,6 +116,9 @@
 tools/bootph: bootpheader $(OBJSTRIP)
 	$(OBJSTRIP) -vb bootpheader tools/bootph
 
+tools/bootpzh: bootpzheader $(OBJSTRIP)
+	$(OBJSTRIP) -vb bootpzheader tools/bootpzh
+
 $(OBJSTRIP): $(OBJSTRIP).c
 	$(HOSTCC) $(HOSTCFLAGS) -I$(HPATH) $(OBJSTRIP).c -o $(OBJSTRIP)
 
@@ -91,6 +131,11 @@
 bootpheader: $(BPOBJECTS)
 	$(LD) $(LINKFLAGS) $(BPOBJECTS) $(LIBS) -o bootpheader
 
+bootpzheader: $(BPZOBJECTS)
+	$(LD) $(LINKFLAGS) $(BPZOBJECTS) $(LIBS) -o bootpzheader
+
+misc.o: misc.c $(TOPDIR)/lib/inflate.c
+
 clean:
 	rm -f $(TARGETS) bootloader bootimage bootpfile bootpheader
 	rm -f tools/mkbb tools/bootlx tools/lxboot tools/bootph
diff -urN linux-2.4.23-pre5/arch/alpha/boot/bootpz.c linux-2.4.23-pre6/arch/alpha/boot/bootpz.c
--- linux-2.4.23-pre5/arch/alpha/boot/bootpz.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/arch/alpha/boot/bootpz.c	2003-10-01 10:38:44.000000000 -0700
@@ -0,0 +1,447 @@
+/*
+ * arch/alpha/boot/bootpz.c
+ *
+ * Copyright (C) 1997 Jay Estabrook
+ *
+ * This file is used for creating a compressed BOOTP file for the
+ * Linux/AXP kernel
+ *
+ * based significantly on the arch/alpha/boot/main.c of Linus Torvalds
+ * and the decompression code from MILO.
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/version.h>
+#include <linux/mm.h>
+
+#include <asm/system.h>
+#include <asm/console.h>
+#include <asm/hwrpb.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+
+#include <stdarg.h>
+
+#include "kzsize.h"
+
+/* FIXME FIXME FIXME */
+#define MALLOC_AREA_SIZE 0x200000 /* 2MB for now */
+/* FIXME FIXME FIXME */
+
+
+#undef DEBUG_CHECK_RANGE
+#define DEBUG_ADDRESSES
+
+#define DEBUG_SP(x) \
+    {register long sp asm("30"); srm_printk("%s (sp=%lx)\n", x, sp);}
+
+extern unsigned long switch_to_osf_pal(unsigned long nr,
+	struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa,
+	unsigned long *vptb);
+
+extern int decompress_kernel(void* destination, void *source,
+			     size_t ksize, size_t kzsize);
+
+struct hwrpb_struct *hwrpb = INIT_HWRPB;
+static struct pcb_struct pcb_va[1];
+
+/*
+ * Find a physical address of a virtual object..
+ *
+ * This is easy using the virtual page table address.
+ */
+#define VPTB	((unsigned long *) 0x200000000)
+
+static inline unsigned long
+find_pa(unsigned long address)
+{
+	unsigned long result;
+
+	result = VPTB[address >> 13];
+	result >>= 32;
+	result <<= 13;
+	result |= address & 0x1fff;
+	return result;
+}	
+
+int
+check_range(unsigned long vstart, unsigned long vend,
+	    unsigned long kstart, unsigned long kend)
+{
+	unsigned long vaddr, kaddr;
+
+#ifdef DEBUG_CHECK_RANGE
+	srm_printk("check_range: V[0x%lx:0x%lx] K[0x%lx:0x%lx]\n",
+		   vstart, vend, kstart, kend);
+#endif
+	/* do some range checking for detecting an overlap... */
+	for (vaddr = vstart; vaddr <= vend; vaddr += PAGE_SIZE)
+	{
+		kaddr = (find_pa(vaddr) | PAGE_OFFSET);
+		if (kaddr >= kstart && kaddr <= kend)
+		{
+#ifdef DEBUG_CHECK_RANGE
+			srm_printk("OVERLAP: vaddr 0x%lx kaddr 0x%lx"
+				   " [0x%lx:0x%lx]\n",
+				   vaddr, kaddr, kstart, kend);
+#endif
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * This function moves into OSF/1 pal-code, and has a temporary
+ * PCB for that. The kernel proper should replace this PCB with
+ * the real one as soon as possible.
+ *
+ * The page table muckery in here depends on the fact that the boot
+ * code has the L1 page table identity-map itself in the second PTE
+ * in the L1 page table. Thus the L1-page is virtually addressable
+ * itself (through three levels) at virtual address 0x200802000.
+ */
+
+#define L1	((unsigned long *) 0x200802000)
+
+void
+pal_init(void)
+{
+	unsigned long i, rev;
+	struct percpu_struct * percpu;
+	struct pcb_struct * pcb_pa;
+
+	/* Create the dummy PCB.  */
+	pcb_va->ksp = 0;
+	pcb_va->usp = 0;
+	pcb_va->ptbr = L1[1] >> 32;
+	pcb_va->asn = 0;
+	pcb_va->pcc = 0;
+	pcb_va->unique = 0;
+	pcb_va->flags = 1;
+	pcb_va->res1 = 0;
+	pcb_va->res2 = 0;
+	pcb_pa = (struct pcb_struct *)find_pa((unsigned long)pcb_va);
+
+	/*
+	 * a0 = 2 (OSF)
+	 * a1 = return address, but we give the asm the vaddr of the PCB
+	 * a2 = physical addr of PCB
+	 * a3 = new virtual page table pointer
+	 * a4 = KSP (but the asm sets it)
+	 */
+	srm_printk("Switching to OSF PAL-code... ");
+
+	i = switch_to_osf_pal(2, pcb_va, pcb_pa, VPTB);
+	if (i) {
+		srm_printk("failed, code %ld\n", i);
+		__halt();
+	}
+
+	percpu = (struct percpu_struct *)
+		(INIT_HWRPB->processor_offset + (unsigned long) INIT_HWRPB);
+	rev = percpu->pal_revision = percpu->palcode_avail[2];
+
+	srm_printk("OK (rev %lx)\n", rev);
+
+	tbia(); /* do it directly in case we are SMP */
+}
+
+/*
+ * Start the kernel.
+ */
+static inline void
+runkernel(void)
+{
+	__asm__ __volatile__(
+		"bis %1,%1,$30\n\t"
+		"bis %0,%0,$27\n\t"
+		"jmp ($27)"
+		: /* no outputs: it doesn't even return */
+		: "r" (START_ADDR),
+		  "r" (PAGE_SIZE + INIT_STACK));
+}
+
+/* Must record the SP (it is virtual) on entry, so we can make sure
+   not to overwrite it during movement or decompression. */
+unsigned long SP_on_entry;
+
+/* Calculate the kernel image address based on the end of the BOOTP
+   bootstrapper (ie this program).
+*/
+extern char _end;
+#define KERNEL_ORIGIN \
+	((((unsigned long)&_end) + 511) & ~511)
+
+/* Round address to next higher page boundary. */
+#define NEXT_PAGE(a)	(((a) | (PAGE_SIZE - 1)) + 1)
+
+#ifdef INITRD_IMAGE_SIZE
+# define REAL_INITRD_SIZE INITRD_IMAGE_SIZE
+#else
+# define REAL_INITRD_SIZE 0
+#endif
+
+/* Defines from include/asm-alpha/system.h
+
+	BOOT_ADDR	Virtual address at which the consoles loads
+			the BOOTP image.
+
+	KERNEL_START    KSEG address at which the kernel is built to run,
+			which includes some initial data pages before the
+			code.
+
+	START_ADDR	KSEG address of the entry point of kernel code.
+
+	ZERO_PGE	KSEG address of page full of zeroes, but 
+			upon entry to kerne cvan be expected
+			to hold the parameter list and possible
+			INTRD information.
+
+   These are used in the local defines below.
+*/
+  
+
+/* Virtual addresses for the BOOTP image. Note that this includes the
+   bootstrapper code as well as the compressed kernel image, and
+   possibly the INITRD image.
+
+   Oh, and do NOT forget the STACK, which appears to be placed virtually
+   beyond the end of the loaded image.
+*/
+#define V_BOOT_IMAGE_START	BOOT_ADDR
+#define V_BOOT_IMAGE_END	SP_on_entry
+
+/* Virtual addresses for just the bootstrapper part of the BOOTP image. */
+#define V_BOOTSTRAPPER_START	BOOT_ADDR
+#define V_BOOTSTRAPPER_END	KERNEL_ORIGIN
+
+/* Virtual addresses for just the data part of the BOOTP
+   image. This may also include the INITRD image, but always
+   includes the STACK.
+*/
+#define V_DATA_START		KERNEL_ORIGIN
+#define V_INITRD_START		(KERNEL_ORIGIN + KERNEL_Z_SIZE)
+#define V_INTRD_END		(V_INITRD_START + REAL_INITRD_SIZE)
+#define V_DATA_END	 	V_BOOT_IMAGE_END
+
+/* KSEG addresses for the uncompressed kernel.
+
+   Note that the end address includes workspace for the decompression.
+   Note also that the DATA_START address is ZERO_PGE, to which we write
+   just before jumping to the kernel image at START_ADDR.
+ */
+#define K_KERNEL_DATA_START	ZERO_PGE
+#define K_KERNEL_IMAGE_START	START_ADDR
+#define K_KERNEL_IMAGE_END	(START_ADDR + KERNEL_SIZE)
+
+/* Define to where we may have to decompress the kernel image, before
+   we move it to the final position, in case of overlap. This will be
+   above the final position of the kernel.
+
+   Regardless of overlap, we move the INITRD image to the end of this
+   copy area, because there needs to be a buffer area after the kernel
+   for "bootmem" anyway.
+*/
+#define K_COPY_IMAGE_START	NEXT_PAGE(K_KERNEL_IMAGE_END)
+#define K_INITRD_START		NEXT_PAGE(K_COPY_IMAGE_START + KERNEL_SIZE)
+#define K_COPY_IMAGE_END \
+    (K_INITRD_START + REAL_INITRD_SIZE + MALLOC_AREA_SIZE)
+#define K_COPY_IMAGE_SIZE \
+    NEXT_PAGE(K_COPY_IMAGE_END - K_COPY_IMAGE_START)
+
+void
+start_kernel(void)
+{
+	int must_move = 0;
+
+	/* Initialize these for the decompression-in-place situation,
+	   which is the smallest amount of work and most likely to
+	   occur when using the normal START_ADDR of the kernel
+	   (currently set to 16MB, to clear all console code.
+	*/
+	unsigned long uncompressed_image_start = K_KERNEL_IMAGE_START;
+	unsigned long uncompressed_image_end = K_KERNEL_IMAGE_END;
+
+	unsigned long initrd_image_start = K_INITRD_START;
+
+	/*
+	 * Note that this crufty stuff with static and envval
+	 * and envbuf is because:
+	 *
+	 * 1. Frequently, the stack is short, and we don't want to overrun;
+	 * 2. Frequently the stack is where we are going to copy the kernel to;
+	 * 3. A certain SRM console required the GET_ENV output to stack.
+	 *    ??? A comment in the aboot sources indicates that the GET_ENV
+	 *    destination must be quadword aligned.  Might this explain the
+	 *    behaviour, rather than requiring output to the stack, which
+	 *    seems rather far-fetched.
+	 */
+	static long nbytes;
+	static char envval[256] __attribute__((aligned(8)));
+	register unsigned long asm_sp asm("30");
+
+	SP_on_entry = asm_sp;
+
+	srm_printk("Linux/Alpha BOOTPZ Loader for Linux " UTS_RELEASE "\n");
+
+	/* Validity check the HWRPB. */
+	if (INIT_HWRPB->pagesize != 8192) {
+		srm_printk("Expected 8kB pages, got %ldkB\n",
+		           INIT_HWRPB->pagesize >> 10);
+		return;
+	}
+	if (INIT_HWRPB->vptb != (unsigned long) VPTB) {
+		srm_printk("Expected vptb at %p, got %p\n",
+			   VPTB, (void *)INIT_HWRPB->vptb);
+		return;
+	}
+
+	/* PALcode (re)initialization. */
+	pal_init();
+
+	/* Get the parameter list from the console environment variable. */
+	nbytes = callback_getenv(ENV_BOOTED_OSFLAGS, envval, sizeof(envval));
+	if (nbytes < 0 || nbytes >= sizeof(envval)) {
+		nbytes = 0;
+	}
+	envval[nbytes] = '\0';
+
+#ifdef DEBUG_ADDRESSES
+	srm_printk("START_ADDR 0x%lx\n", START_ADDR);
+	srm_printk("KERNEL_ORIGIN 0x%lx\n", KERNEL_ORIGIN);
+	srm_printk("KERNEL_SIZE 0x%x\n", KERNEL_SIZE);
+	srm_printk("KERNEL_Z_SIZE 0x%x\n", KERNEL_Z_SIZE);
+#endif
+
+	/* Since all the SRM consoles load the BOOTP image at virtual
+	 * 0x20000000, we have to ensure that the physical memory
+	 * pages occupied by that image do NOT overlap the physical
+	 * address range where the kernel wants to be run.  This
+	 * causes real problems when attempting to cdecompress the
+	 * former into the latter... :-(
+	 *
+	 * So, we may have to decompress/move the kernel/INITRD image
+	 * virtual-to-physical someplace else first before moving
+	 * kernel /INITRD to their final resting places... ;-}
+	 *
+	 * Sigh...
+	 */
+
+	/* First, check to see if the range of addresses occupied by
+	   the bootstrapper part of the BOOTP image include any of the
+	   physical pages into which the kernel will be placed for
+	   execution.
+
+	   We only need check on the final kernel image range, since we
+	   will put the INITRD someplace that we can be sure is not
+	   in conflict.
+	 */
+	if (check_range(V_BOOTSTRAPPER_START, V_BOOTSTRAPPER_END,
+			K_KERNEL_DATA_START, K_KERNEL_IMAGE_END))
+	{
+		srm_printk("FATAL ERROR: overlap of bootstrapper code\n");
+		__halt();
+	}
+
+	/* Next, check to see if the range of addresses occupied by
+	   the compressed kernel/INITRD/stack portion of the BOOTP
+	   image include any of the physical pages into which the
+	   decompressed kernel or the INITRD will be placed for
+	   execution.
+	 */
+	if (check_range(V_DATA_START, V_DATA_END,
+			K_KERNEL_IMAGE_START, K_COPY_IMAGE_END))
+	{
+#ifdef DEBUG_ADDRESSES
+		srm_printk("OVERLAP: cannot decompress in place\n");
+#endif
+		uncompressed_image_start = K_COPY_IMAGE_START;
+		uncompressed_image_end = K_COPY_IMAGE_END;
+		must_move = 1;
+
+		/* Finally, check to see if the range of addresses
+		   occupied by the compressed kernel/INITRD part of
+		   the BOOTP image include any of the physical pages
+		   into which that part is to be copied for
+		   decompression.
+		*/
+		while (check_range(V_DATA_START, V_DATA_END,
+				   uncompressed_image_start,
+				   uncompressed_image_end))
+		{
+#if 0
+			uncompressed_image_start += K_COPY_IMAGE_SIZE;
+			uncompressed_image_end += K_COPY_IMAGE_SIZE;
+			initrd_image_start += K_COPY_IMAGE_SIZE;
+#else
+			/* Keep as close as possible to end of BOOTP image. */
+			uncompressed_image_start += PAGE_SIZE;
+			uncompressed_image_end += PAGE_SIZE;
+			initrd_image_start += PAGE_SIZE;
+#endif
+		}
+	}
+
+	srm_printk("Starting to load the kernel with args '%s'\n", envval);
+
+#ifdef DEBUG_ADDRESSES
+	srm_printk("Decompressing the kernel...\n"
+		   "...from 0x%lx to 0x%lx size 0x%x\n",
+		   V_DATA_START,
+		   uncompressed_image_start,
+		   KERNEL_SIZE);
+#endif
+        decompress_kernel((void *)uncompressed_image_start,
+			  (void *)V_DATA_START,
+			  KERNEL_SIZE, KERNEL_Z_SIZE);
+
+	/*
+	 * Now, move things to their final positions, if/as required.
+	 */
+
+#ifdef INITRD_IMAGE_SIZE
+
+	/* First, we always move the INITRD image, if present. */
+#ifdef DEBUG_ADDRESSES
+	srm_printk("Moving the INITRD image...\n"
+		   " from 0x%lx to 0x%lx size 0x%x\n",
+		   V_INITRD_START,
+		   initrd_image_start,
+		   INITRD_IMAGE_SIZE);
+#endif
+	memcpy((void *)initrd_image_start,
+	       (void *)V_INITRD_START,
+	       INITRD_IMAGE_SIZE);
+
+#endif /* INITRD_IMAGE_SIZE */
+
+	/* Next, we may have to move the uncompressed kernel to the
+	   final destination.
+	 */
+	if (must_move) {
+#ifdef DEBUG_ADDRESSES
+		srm_printk("Moving the uncompressed kernel...\n"
+			   "...from 0x%lx to 0x%lx size 0x%x\n",
+			   uncompressed_image_start,
+			   K_KERNEL_IMAGE_START,
+			   (unsigned)KERNEL_SIZE);
+#endif
+		memcpy((void *)K_KERNEL_IMAGE_START,
+		       (void *)uncompressed_image_start,
+		       KERNEL_SIZE);
+	}
+	
+	/* Clear the zero page, then move the argument list in. */
+	memset((char*)ZERO_PGE, 0, PAGE_SIZE);
+	strcpy((char*)ZERO_PGE, envval);
+
+#ifdef INITRD_IMAGE_SIZE
+	/* Finally, set the INITRD paramenters for the kernel. */
+	((long *)(ZERO_PGE+256))[0] = initrd_image_start;
+	((long *)(ZERO_PGE+256))[1] = INITRD_IMAGE_SIZE;
+
+#endif /* INITRD_IMAGE_SIZE */
+
+	runkernel();
+}
diff -urN linux-2.4.23-pre5/arch/alpha/boot/misc.c linux-2.4.23-pre6/arch/alpha/boot/misc.c
--- linux-2.4.23-pre5/arch/alpha/boot/misc.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/arch/alpha/boot/misc.c	2003-10-01 10:38:44.000000000 -0700
@@ -0,0 +1,207 @@
+/*
+ * misc.c
+ * 
+ * This is a collection of several routines from gzip-1.0.3 
+ * adapted for Linux.
+ *
+ * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
+ *
+ * Modified for ARM Linux by Russell King
+ *
+ * Nicolas Pitre <nico@visuaide.com>  1999/04/14 :
+ *  For this code to run directly from Flash, all constant variables must
+ *  be marked with 'const' and all other variables initialized at run-time 
+ *  only.  This way all non constant variables will end up in the bss segment,
+ *  which should point to addresses in RAM and cleared to 0 on start.
+ *  This allows for a much quicker boot time.
+ *
+ * Modified for Alpha, from the ARM version, by Jay Estabrook 2003.
+ */
+
+#include <linux/kernel.h>
+
+#include <asm/uaccess.h>
+
+#define memzero(s,n)	memset ((s),0,(n))
+#define puts		srm_printk
+extern long srm_printk(const char *, ...)
+     __attribute__ ((format (printf, 1, 2)));;
+
+/*
+ * gzip delarations
+ */
+#define OF(args)  args
+#define STATIC static
+
+typedef unsigned char  uch;
+typedef unsigned short ush;
+typedef unsigned long  ulg;
+
+#define WSIZE 0x8000		/* Window size must be at least 32k, */
+				/* and a power of two */
+
+static uch *inbuf;		/* input buffer */
+static uch *window;		/* Sliding window buffer */
+
+static unsigned insize;		/* valid bytes in inbuf */
+static unsigned inptr;		/* index of next byte to be processed in inbuf */
+static unsigned outcnt;		/* bytes in output buffer */
+
+/* gzip flag byte */
+#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
+#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
+#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
+#define COMMENT      0x10 /* bit 4 set: file comment present */
+#define ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
+#define RESERVED     0xC0 /* bit 6,7:   reserved */
+
+#define get_byte()  (inptr < insize ? inbuf[inptr++] : fill_inbuf())
+
+/* Diagnostic functions */
+#ifdef DEBUG
+#  define Assert(cond,msg) {if(!(cond)) error(msg);}
+#  define Trace(x) fprintf x
+#  define Tracev(x) {if (verbose) fprintf x ;}
+#  define Tracevv(x) {if (verbose>1) fprintf x ;}
+#  define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
+#  define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
+#else
+#  define Assert(cond,msg)
+#  define Trace(x)
+#  define Tracev(x)
+#  define Tracevv(x)
+#  define Tracec(c,x)
+#  define Tracecv(c,x)
+#endif
+
+static int  fill_inbuf(void);
+static void flush_window(void);
+static void error(char *m);
+static void gzip_mark(void **);
+static void gzip_release(void **);
+
+static char *input_data;
+static int  input_data_size;
+
+static uch *output_data;
+static ulg output_ptr;
+static ulg bytes_out;
+
+static void *malloc(int size);
+static void free(void *where);
+static void error(char *m);
+static void gzip_mark(void **);
+static void gzip_release(void **);
+
+extern int end;
+static ulg free_mem_ptr;
+static ulg free_mem_ptr_end;
+
+#define HEAP_SIZE 0x2000
+
+#include "../../../lib/inflate.c"
+
+static void *malloc(int size)
+{
+	void *p;
+
+	if (size <0) error("Malloc error\n");
+	if (free_mem_ptr <= 0) error("Memory error\n");
+
+	free_mem_ptr = (free_mem_ptr + 3) & ~3;	/* Align */
+
+	p = (void *)free_mem_ptr;
+	free_mem_ptr += size;
+
+	if (free_mem_ptr >= free_mem_ptr_end)
+		error("Out of memory");
+	return p;
+}
+
+static void free(void *where)
+{ /* gzip_mark & gzip_release do the free */
+}
+
+static void gzip_mark(void **ptr)
+{
+	*ptr = (void *) free_mem_ptr;
+}
+
+static void gzip_release(void **ptr)
+{
+	free_mem_ptr = (long) *ptr;
+}
+
+/* ===========================================================================
+ * Fill the input buffer. This is called only when the buffer is empty
+ * and at least one byte is really needed.
+ */
+int fill_inbuf(void)
+{
+	if (insize != 0)
+		error("ran out of input data\n");
+
+	inbuf = input_data;
+	insize = input_data_size;
+
+	inptr = 1;
+	return inbuf[0];
+}
+
+/* ===========================================================================
+ * Write the output window window[0..outcnt-1] and update crc and bytes_out.
+ * (Used for the decompressed data only.)
+ */
+void flush_window(void)
+{
+	ulg c = crc;
+	unsigned n;
+	uch *in, *out, ch;
+
+	in = window;
+	out = &output_data[output_ptr];
+	for (n = 0; n < outcnt; n++) {
+		ch = *out++ = *in++;
+		c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
+	}
+	crc = c;
+	bytes_out += (ulg)outcnt;
+	output_ptr += (ulg)outcnt;
+	outcnt = 0;
+/*	puts("."); */
+}
+
+static void error(char *x)
+{
+	puts("\n\n");
+	puts(x);
+	puts("\n\n -- System halted");
+
+	while(1);	/* Halt */
+}
+
+unsigned int
+decompress_kernel(void *output_start,
+		  void *input_start,
+		  size_t ksize,
+		  size_t kzsize)
+{
+	output_data		= (uch *)output_start;
+	input_data		= (uch *)input_start;
+	input_data_size		= kzsize; /* use compressed size */
+
+	/* FIXME FIXME FIXME */
+	free_mem_ptr		= (ulg)output_start + ksize;
+	free_mem_ptr_end	= (ulg)output_start + ksize + 0x200000;
+	/* FIXME FIXME FIXME */
+
+	/* put in temp area to reduce initial footprint */
+	window = malloc(WSIZE);
+
+	makecrc();
+/*	puts("Uncompressing Linux..."); */
+	gunzip();
+/*	puts(" done, booting the kernel.\n"); */
+	return output_ptr;
+}
diff -urN linux-2.4.23-pre5/arch/alpha/boot/tools/objstrip.c linux-2.4.23-pre6/arch/alpha/boot/tools/objstrip.c
--- linux-2.4.23-pre5/arch/alpha/boot/tools/objstrip.c	2001-05-21 13:41:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/alpha/boot/tools/objstrip.c	2003-10-01 10:38:44.000000000 -0700
@@ -27,7 +27,6 @@
 #include <linux/param.h>
 #include <linux/string.h>
 #ifdef __ELF__
-# include <asm/elf.h>
 # include <linux/elf.h>
 #endif
 
diff -urN linux-2.4.23-pre5/arch/alpha/config.in linux-2.4.23-pre6/arch/alpha/config.in
--- linux-2.4.23-pre5/arch/alpha/config.in	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/arch/alpha/config.in	2003-10-01 10:38:44.000000000 -0700
@@ -440,7 +440,16 @@
 mainmenu_option next_comment
 comment 'Kernel hacking'
 
-bool 'Legacy kernel start address' CONFIG_ALPHA_LEGACY_START_ADDRESS
+if [ "$CONFIG_ALPHA_TITAN" = "y" -o "$CONFIG_ALPHA_WILDFIRE" = "y" -o \
+     "$CONFIG_ALPHA_MARVEL" = "y" ]; then
+   define_bool CONFIG_ALPHA_LEGACY_START_ADDRESS n
+else
+   if [ "$CONFIG_ALPHA_GENERIC" = "y" ]; then
+      bool 'Legacy kernel start address' CONFIG_ALPHA_LEGACY_START_ADDRESS
+   else
+      define_bool CONFIG_ALPHA_LEGACY_START_ADDRESS y
+   fi
+fi
 
 bool 'Kernel debugging' CONFIG_DEBUG_KERNEL
 if [ "$CONFIG_DEBUG_KERNEL" != "n" ]; then
diff -urN linux-2.4.23-pre5/arch/alpha/kernel/Makefile linux-2.4.23-pre6/arch/alpha/kernel/Makefile
--- linux-2.4.23-pre5/arch/alpha/kernel/Makefile	2003-08-25 04:44:39.000000000 -0700
+++ linux-2.4.23-pre6/arch/alpha/kernel/Makefile	2003-10-01 10:38:44.000000000 -0700
@@ -37,17 +37,21 @@
 ifdef CONFIG_ALPHA_GENERIC
 
 obj-y 	 += core_apecs.o core_cia.o core_irongate.o core_lca.o core_mcpcia.o \
-	    core_polaris.o core_t2.o core_tsunami.o core_titan.o \
-	    core_wildfire.o core_marvel.o \
+	    core_polaris.o core_t2.o core_tsunami.o \
 	    sys_alcor.o sys_cabriolet.o sys_dp264.o sys_eb64p.o sys_eiger.o \
-	    sys_jensen.o sys_miata.o sys_mikasa.o sys_nautilus.o sys_titan.o \
+	    sys_jensen.o sys_miata.o sys_mikasa.o sys_nautilus.o \
 	    sys_noritake.o sys_rawhide.o sys_ruffian.o sys_rx164.o \
 	    sys_sable.o sys_sio.o sys_sx164.o sys_takara.o sys_rx164.o \
-	    sys_wildfire.o sys_marvel.o \
-	    err_titan.o err_marvel.o \
 	    irq_pyxis.o srmcons.o gct.o \
 	    es1888.o smc37c669.o smc37c93x.o ns87312.o
 
+ifndef CONFIG_ALPHA_LEGACY_START_ADDRESS
+obj-y 	 += core_titan.o core_wildfire.o core_marvel.o \
+	    sys_titan.o sys_wildfire.o sys_marvel.o \
+	    err_titan.o err_marvel.o
+
+endif
+
 else
 
 # Misc support
diff -urN linux-2.4.23-pre5/arch/alpha/kernel/setup.c linux-2.4.23-pre6/arch/alpha/kernel/setup.c
--- linux-2.4.23-pre5/arch/alpha/kernel/setup.c	2003-08-25 04:44:39.000000000 -0700
+++ linux-2.4.23-pre6/arch/alpha/kernel/setup.c	2003-10-01 10:38:44.000000000 -0700
@@ -583,6 +583,35 @@
 	       var_name, alpha_mv.vector_name,
 	       (alpha_using_srm ? "SRM" : "MILO"));
 
+	printk("Major Options: "
+#ifdef CONFIG_SMP
+	       "SMP "
+#endif
+#ifdef CONFIG_ALPHA_EV56
+	       "EV56 "
+#endif
+#ifdef CONFIG_ALPHA_EV67
+	       "EV67 "
+#endif
+#ifdef CONFIG_ALPHA_LEGACY_START_ADDRESS
+	       "LEGACY_START "
+#endif
+
+#ifdef CONFIG_DISCONTIGMEM
+	       "DISCONTIGMEM "
+#ifdef CONFIG_NUMA
+	       "NUMA "
+#endif
+#endif
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+	       "DEBUG_SPINLOCK "
+#endif
+#ifdef CONFIG_MAGIC_SYSRQ
+	       "MAGIC_SYSRQ "
+#endif
+	       "\n");
+
 	printk("Command line: %s\n", command_line);
 
 	/* 
diff -urN linux-2.4.23-pre5/arch/alpha/kernel/sys_nautilus.c linux-2.4.23-pre6/arch/alpha/kernel/sys_nautilus.c
--- linux-2.4.23-pre5/arch/alpha/kernel/sys_nautilus.c	2003-08-25 04:44:39.000000000 -0700
+++ linux-2.4.23-pre6/arch/alpha/kernel/sys_nautilus.c	2003-10-01 10:38:44.000000000 -0700
@@ -226,11 +226,13 @@
 	if (request_resource(&iomem_resource, bus->resource[1]) < 0)
 		printk(KERN_ERR "Failed to request MEM on hose 0\n");
 
-	if (pci_mem < memtop && pci_mem > alpha_mv.min_mem_address) {
+	if (pci_mem < memtop)
+		memtop = pci_mem;
+	if (memtop > alpha_mv.min_mem_address) {
 		free_reserved_mem(__va(alpha_mv.min_mem_address),
-				  __va(pci_mem));
+				  __va(memtop));
 		printk("nautilus_init_pci: %ldk freed\n",
-			(pci_mem - alpha_mv.min_mem_address) >> 10);
+			(memtop - alpha_mv.min_mem_address) >> 10);
 	}
 
 	if ((IRONGATE0->dev_vendor >> 16) > 0x7006)	/* Albacore? */
diff -urN linux-2.4.23-pre5/arch/alpha/mm/init.c linux-2.4.23-pre6/arch/alpha/mm/init.c
--- linux-2.4.23-pre5/arch/alpha/mm/init.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.23-pre6/arch/alpha/mm/init.c	2003-10-01 10:38:44.000000000 -0700
@@ -234,7 +234,8 @@
 	/* Allocate one PGD and one PMD.  In the case of SRM, we'll need
 	   these to actually remap the console.  There is an assumption
 	   here that only one of each is needed, and this allows for 8MB.
-	   Currently (late 1999), big consoles are still under 4MB.
+	   On systems with larger consoles, additional pages will be
+	   allocated as needed during the mapping process.
 
 	   In the case of not SRM, but not CONFIG_ALPHA_LARGE_VMALLOC,
 	   we need to allocate the PGD we use for vmalloc before we start
@@ -261,6 +262,15 @@
 			unsigned long paddr = crb->map[i].pa;
 			crb->map[i].va = vaddr;
 			for (j = 0; j < crb->map[i].count; ++j) {
+				/* Newer console's (especially on larger
+				   systems) may require more pages of
+				   PTEs. Grab additional pages as needed. */
+				if (pmd != pmd_offset(pgd, vaddr)) {
+					memset(kernel_end, 0, PAGE_SIZE);
+					pmd = pmd_offset(pgd, vaddr);
+					pmd_set(pmd, (pte_t *)kernel_end);
+					kernel_end += PAGE_SIZE;
+				}
 				set_pte(pte_offset(pmd, vaddr),
 					mk_pte_phys(paddr, PAGE_KERNEL));
 				paddr += PAGE_SIZE;
diff -urN linux-2.4.23-pre5/arch/i386/kernel/mpparse.c linux-2.4.23-pre6/arch/i386/kernel/mpparse.c
--- linux-2.4.23-pre5/arch/i386/kernel/mpparse.c	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/arch/i386/kernel/mpparse.c	2003-10-01 10:38:44.000000000 -0700
@@ -229,6 +229,11 @@
 		boot_cpu_logical_apicid = logical_apicid;
 	}
 
+	if (num_processors >= NR_CPUS){
+		printk(KERN_WARNING "NR_CPUS limit of %i reached. Cannot "
+			"boot CPU(apicid 0x%x).\n", NR_CPUS, m->mpc_apicid);
+		return;
+	}
 	num_processors++;
 
 	if (m->mpc_apicid > MAX_APICS) {
diff -urN linux-2.4.23-pre5/arch/i386/kernel/setup.c linux-2.4.23-pre6/arch/i386/kernel/setup.c
--- linux-2.4.23-pre5/arch/i386/kernel/setup.c	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/arch/i386/kernel/setup.c	2003-10-01 10:38:44.000000000 -0700
@@ -2291,6 +2291,8 @@
 	{ 0x83, LVL_2,      512 },
 	{ 0x84, LVL_2,      1024 },
 	{ 0x85, LVL_2,      2048 },
+	{ 0x86, LVL_2,      512 },
+	{ 0x87, LVL_2,      1024 },
 	{ 0x00, 0, 0}
 };
 
diff -urN linux-2.4.23-pre5/arch/ppc/8xx_io/Config.in linux-2.4.23-pre6/arch/ppc/8xx_io/Config.in
--- linux-2.4.23-pre5/arch/ppc/8xx_io/Config.in	2001-10-15 13:35:26.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/8xx_io/Config.in	2003-10-01 10:38:44.000000000 -0700
@@ -2,8 +2,14 @@
 # MPC8xx Communication options
 #
 mainmenu_option next_comment
-comment 'MPC8xx CPM Options'
+comment 'MPC8xx Options'
 
+comment 'Generic MPC8xx Options'
+bool 'Copy-Back Data Cache (else Writethrough)' CONFIG_8xx_COPYBACK
+bool 'CPU6 Silicon Errata (860 Pre Rev. C)' CONFIG_8xx_CPU6
+bool 'I2C/SPI Microcode Patch' CONFIG_UCODE_PATCH
+
+comment 'MPC8xx CPM Options'
 if [ "$CONFIG_NET_ETHERNET" = "y" ]; then
   bool 'CPM SCC Ethernet' CONFIG_SCC_ENET
   if [ "$CONFIG_SCC_ENET" = "y" ]; then
@@ -12,25 +18,28 @@
 	 SCC2	CONFIG_SCC2_ENET	\
 	 SCC3	CONFIG_SCC3_ENET"	SCC1
   fi
-  bool '860T FEC Ethernet' CONFIG_FEC_ENET
+  bool '8xxT FEC Ethernet' CONFIG_FEC_ENET
   if [ "$CONFIG_FEC_ENET" = "y" ]; then
     bool 'Use MDIO for PHY configuration' CONFIG_USE_MDIO
   fi
   bool 'Use Big CPM Ethernet Buffers' CONFIG_ENET_BIG_BUFFERS
 fi
+if [ "$CONFIG_SCC3_ENET" != "y" ]; then
+  bool 'Use SMC1 for UART' CONFIG_8xx_SMC1
+fi
 bool 'Use SMC2 for UART' CONFIG_SMC2_UART
 if [ "$CONFIG_SMC2_UART" = "y" ]; then
   bool 'Use Alternate SMC2 I/O (823/850)' CONFIG_ALTSMC2
   bool 'Use SMC2 for Console' CONFIG_CONS_SMC2
 fi
-bool 'Enable SCC2 and SCC3 for UART' CONFIG_USE_SCC_IO
-
-# This doesn't really belong here, but it is convenient to ask
-# 8xx specific questions.
-
-comment 'Generic MPC8xx Options'
-bool 'Copy-Back Data Cache (else Writethrough)' CONFIG_8xx_COPYBACK
-bool 'CPU6 Silicon Errata (860 Pre Rev. C)' CONFIG_8xx_CPU6
-bool 'I2C/SPI Microcode Patch' CONFIG_UCODE_PATCH
-
+if [ "$CONFIG_SCC1_ENET" != "y" ]; then
+  bool 'Use SCC1 for UART' CONFIG_8xx_SCC1 
+fi
+if [ "$CONFIG_SCC2_ENET" != "y" ]; then
+  bool 'Use SCC2 for UART' CONFIG_8xx_SCC2
+fi
+if [ "$CONFIG_SCC3_ENET" != "y" ]; then
+  bool 'Use SCC3 for UART' CONFIG_8xx_SCC3
+fi
+bool 'Use SCC4 for UART' CONFIG_8xx_SCC4
 endmenu
diff -urN linux-2.4.23-pre5/arch/ppc/8xx_io/commproc.c linux-2.4.23-pre6/arch/ppc/8xx_io/commproc.c
--- linux-2.4.23-pre5/arch/ppc/8xx_io/commproc.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/8xx_io/commproc.c	2003-10-01 10:38:44.000000000 -0700
@@ -54,6 +54,7 @@
 static	struct	cpm_action cpm_vecs[CPMVEC_NR];
 static	void	cpm_interrupt(int irq, void * dev, struct pt_regs * regs);
 static	void	cpm_error_interrupt(int irq, void *, struct pt_regs * regs);
+static	void	alloc_host_memory(void);
 
 /* Define a table of names to identify CPM interrupt handlers in
  * /proc/interrupts.
@@ -104,7 +105,7 @@
 };
 
 void
-m8xx_cpm_reset(uint host_page_addr)
+m8xx_cpm_reset()
 {
 	volatile immap_t	 *imp;
 	volatile cpm8xx_t	*commproc;
@@ -130,7 +131,7 @@
 	 * this is what we realy want for some applications, but the
 	 * manual recommends it.
 	 * Bit 25, FAM can also be set to use FEC aggressive mode (860T).
-	*/
+	 */
 	imp->im_siu_conf.sc_sdcr = 1;
 
 	/* Reclaim the DP memory for our use.
@@ -138,25 +139,23 @@
 	dp_alloc_base = CPM_DATAONLY_BASE;
 	dp_alloc_top = dp_alloc_base + CPM_DATAONLY_SIZE;
 
-	/* Set the host page for allocation.
+	/* Tell everyone where the comm processor resides.
 	*/
-	host_buffer = host_page_addr;	/* Host virtual page address */
-	host_end = host_page_addr + PAGE_SIZE;
+	cpmp = (cpm8xx_t *)commproc;
+}
 
-	/* We need to get this page early, so I have to do it the
-	 * hard way.
-	 */
-	if (get_pteptr(&init_mm, host_page_addr, &pte)) {
-		pte_val(*pte) |= _PAGE_NO_CACHE;
-		flush_tlb_page(init_mm.mmap, host_buffer);
-	}
-	else {
-		panic("Huh?  No CPM host page?");
-	}
+/* We used to do this earlier, but have to postpone as long as possible
+ * to ensure the kernel VM is now running.
+ */
+static void
+alloc_host_memory()
+{
+	uint	physaddr;
 
-	/* Tell everyone where the comm processor resides.
+	/* Set the host page for allocation.
 	*/
-	cpmp = (cpm8xx_t *)commproc;
+	host_buffer = (uint)consistent_alloc(GFP_KERNEL, PAGE_SIZE, &physaddr);
+	host_end = host_buffer + PAGE_SIZE;
 }
 
 /* This is called during init_IRQ.  We used to do it above, but this
@@ -321,6 +320,9 @@
 {
 	uint	retloc;
 
+	if (host_buffer == 0)
+		alloc_host_memory();
+
 	if ((host_buffer + size) >= host_end)
 		return(0);
 
diff -urN linux-2.4.23-pre5/arch/ppc/8xx_io/enet.c linux-2.4.23-pre6/arch/ppc/8xx_io/enet.c
--- linux-2.4.23-pre5/arch/ppc/8xx_io/enet.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/8xx_io/enet.c	2003-10-01 10:38:44.000000000 -0700
@@ -137,6 +137,11 @@
 	cbd_t	*cur_rx, *cur_tx;		/* The next free ring entry */
 	cbd_t	*dirty_tx;	/* The ring entries to be free()ed. */
 	scc_t	*sccp;
+
+	/* Virtual addresses for the receive buffers because we can't
+	 * do a __va() on them anymore.
+	 */
+	unsigned char *rx_vaddr[RX_RING_SIZE];
 	struct	net_device_stats stats;
 	uint	tx_free;
 	spinlock_t lock;
@@ -498,7 +503,7 @@
 			skb->dev = dev;
 			skb_put(skb,pkt_len-4);	/* Make room */
 			eth_copy_and_sum(skb,
-				(unsigned char *)__va(bdp->cbd_bufaddr),
+				cep->rx_vaddr[bdp - cep->rx_bd_base],
 				pkt_len-4, 0);
 			skb->protocol=eth_type_trans(skb,dev);
 			netif_rx(skb);
@@ -632,10 +637,9 @@
 {
 	struct net_device *dev;
 	struct scc_enet_private *cep;
-	int i, j;
-	unsigned char	*eap;
-	unsigned long	mem_addr;
-	pte_t		*pte;
+	int i, j, k;
+	unsigned char	*eap, *ba;
+	dma_addr_t	mem_addr;
 	bd_t		*bd;
 	volatile	cbd_t		*bdp;
 	volatile	cpm8xx_t	*cp;
@@ -826,24 +830,21 @@
 	bdp->cbd_sc |= BD_SC_WRAP;
 
 	bdp = cep->rx_bd_base;
+	k = 0;
 	for (i=0; i<CPM_ENET_RX_PAGES; i++) {
 
 		/* Allocate a page.
 		*/
-		mem_addr = __get_free_page(GFP_KERNEL);
-
-		/* Make it uncached.
-		*/
-		pte = va_to_pte(mem_addr);
-		pte_val(*pte) |= _PAGE_NO_CACHE;
-		flush_tlb_page(init_mm.mmap, mem_addr);
+		ba = (unsigned char *)consistent_alloc(GFP_KERNEL, PAGE_SIZE, &mem_addr);
 
 		/* Initialize the BD for every fragment in the page.
 		*/
 		for (j=0; j<CPM_ENET_RX_FRPPG; j++) {
 			bdp->cbd_sc = BD_ENET_RX_EMPTY | BD_ENET_RX_INTR;
-			bdp->cbd_bufaddr = __pa(mem_addr);
+			bdp->cbd_bufaddr = mem_addr;
+			cep->rx_vaddr[k++] = ba;
 			mem_addr += CPM_ENET_RX_FRSIZE;
+			ba += CPM_ENET_RX_FRSIZE;
 			bdp++;
 		}
 	}
diff -urN linux-2.4.23-pre5/arch/ppc/8xx_io/fec.c linux-2.4.23-pre6/arch/ppc/8xx_io/fec.c
--- linux-2.4.23-pre5/arch/ppc/8xx_io/fec.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/8xx_io/fec.c	2003-10-01 10:38:44.000000000 -0700
@@ -161,7 +161,12 @@
 	cbd_t	*tx_bd_base;
 	cbd_t	*cur_rx, *cur_tx;		/* The next free ring entry */
 	cbd_t	*dirty_tx;	/* The ring entries to be free()ed. */
-	scc_t	*sccp;
+
+	/* Virtual addresses for the receive buffers because we can't
+	 * do a __va() on them anymore.
+	 */
+	unsigned char *rx_vaddr[RX_RING_SIZE];
+
 	struct	net_device_stats stats;
 	uint	tx_free;
 	spinlock_t lock;
@@ -587,7 +592,7 @@
 	fep->stats.rx_packets++;
 	pkt_len = bdp->cbd_datlen;
 	fep->stats.rx_bytes += pkt_len;
-	data = (__u8*)__va(bdp->cbd_bufaddr);
+	data = fep->rx_vaddr[bdp - fep->rx_bd_base];
 
 	/* This does 16 byte alignment, exactly what we need.
 	 * The packet length includes FCS, but we don't want to
@@ -602,9 +607,7 @@
 	} else {
 		skb->dev = dev;
 		skb_put(skb,pkt_len-4);	/* Make room */
-		eth_copy_and_sum(skb,
-				 (unsigned char *)__va(bdp->cbd_bufaddr),
-				 pkt_len-4, 0);
+		eth_copy_and_sum(skb, data, pkt_len-4, 0);
 		skb->protocol=eth_type_trans(skb,dev);
 		netif_rx(skb);
 	}
@@ -1700,10 +1703,9 @@
 {
 	struct net_device *dev;
 	struct fec_enet_private *fep;
-	int i, j;
-	unsigned char	*eap, *iap;
-	unsigned long	mem_addr;
-	pte_t		*pte;
+	int i, j, k;
+	unsigned char	*eap, *iap, *ba;
+	dma_addr_t	mem_addr;
 	volatile	cbd_t	*bdp;
 	cbd_t		*cbd_base;
 	volatile	immap_t	*immap;
@@ -1774,14 +1776,7 @@
 		printk("FEC initialization failed.\n");
 		return 1;
 	}
-	mem_addr = __get_free_page(GFP_KERNEL);
-	cbd_base = (cbd_t *)mem_addr;
-
-	/* Make it uncached.
-	*/
-	pte = va_to_pte(mem_addr);
-	pte_val(*pte) |= _PAGE_NO_CACHE;
-	flush_tlb_page(init_mm.mmap, mem_addr);
+	cbd_base = (cbd_t *)consistent_alloc(GFP_KERNEL, PAGE_SIZE, &mem_addr);
 
 	/* Set receive and transmit descriptor base.
 	*/
@@ -1793,24 +1788,21 @@
 	/* Initialize the receive buffer descriptors.
 	*/
 	bdp = fep->rx_bd_base;
+	k = 0;
 	for (i=0; i<FEC_ENET_RX_PAGES; i++) {
 
 		/* Allocate a page.
 		*/
-		mem_addr = __get_free_page(GFP_KERNEL);
-
-		/* Make it uncached.
-		*/
-		pte = va_to_pte(mem_addr);
-		pte_val(*pte) |= _PAGE_NO_CACHE;
-		flush_tlb_page(init_mm.mmap, mem_addr);
+		ba = (unsigned char *)consistent_alloc(GFP_KERNEL, PAGE_SIZE, &mem_addr);
 
 		/* Initialize the BD for every fragment in the page.
 		*/
 		for (j=0; j<FEC_ENET_RX_FRPPG; j++) {
 			bdp->cbd_sc = BD_ENET_RX_EMPTY;
-			bdp->cbd_bufaddr = __pa(mem_addr);
+			bdp->cbd_bufaddr = mem_addr;
+			fep->rx_vaddr[k++] = ba;
 			mem_addr += FEC_ENET_RX_FRSIZE;
+			ba += FEC_ENET_RX_FRSIZE;
 			bdp++;
 		}
 	}
@@ -1974,8 +1966,8 @@
 
 	/* Set receive and transmit descriptor base.
 	*/
-	fecp->fec_r_des_start = __pa((uint)(fep->rx_bd_base));
-	fecp->fec_x_des_start = __pa((uint)(fep->tx_bd_base));
+	fecp->fec_r_des_start = iopa((uint)(fep->rx_bd_base));
+	fecp->fec_x_des_start = iopa((uint)(fep->tx_bd_base));
 
 	fep->dirty_tx = fep->cur_tx = fep->tx_bd_base;
 	fep->tx_free = TX_RING_SIZE;
diff -urN linux-2.4.23-pre5/arch/ppc/8xx_io/uart.c linux-2.4.23-pre6/arch/ppc/8xx_io/uart.c
--- linux-2.4.23-pre5/arch/ppc/8xx_io/uart.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/8xx_io/uart.c	2003-10-01 10:38:44.000000000 -0700
@@ -126,10 +126,27 @@
  * type of SMC or SCC.
  * The SMCs do not support any modem control signals.
  */
-#define smc_scc_num	hub6
-#define NUM_IS_SCC	((int)0x00010000)
-#define PORT_NUM(P)	((P) & 0x0000ffff)
-#define PORT_IS_SCC(P)	((P) & NUM_IS_SCC)
+#define smc_scc_num	     hub6
+#define NUM_IS_SCC	     ((int)0x000100000)
+#define NUM_BRG              ((int)0x0000FF00)
+#define NUM_BRG_SHIFT        8
+#define NUM                  ((int)0x000000FF)
+#define NUM_SHIFT            0
+#define PORT_NUM(P)	     ((P) & NUM)
+#define PORT_NUM_SET(N)      (((N)-1) << NUM_SHIFT)
+#define PORT_IS_SCC(P)	     ((P) & NUM_IS_SCC)
+#define PORT_BRG(P)          (((P) & NUM_BRG) >> NUM_BRG_SHIFT)
+#define PORT_BRG_SET(P,B)    (P) = (((P) & ~NUM_BRG) | ((B) << NUM_BRG_SHIFT))
+
+/* Short names for the ports
+*/
+#define QUICC_CPM_SMC1  (PORT_NUM_SET(1))
+#define QUICC_CPM_SMC2  (PORT_NUM_SET(2))
+#define QUICC_CPM_SCC1  (PORT_NUM_SET(1)|NUM_IS_SCC)
+#define QUICC_CPM_SCC2  (PORT_NUM_SET(2)|NUM_IS_SCC)
+#define QUICC_CPM_SCC3  (PORT_NUM_SET(3)|NUM_IS_SCC)
+#define QUICC_CPM_SCC4  (PORT_NUM_SET(4)|NUM_IS_SCC)
+#define QUICC_MAX_BRG   3  /* BRG1..BRG4 */
 
 /* The serial port to use for KGDB. */
 #ifdef CONFIG_KGDB_TTYS1
@@ -151,22 +168,24 @@
  */
 static struct serial_state rs_table[] = {
 	/* UART CLK   PORT          IRQ      FLAGS  NUM   */
-#ifndef CONFIG_SCC3_ENET	/* SMC1 not usable with Ethernet on SCC3 */
-  	{ 0,     0, PROFF_SMC1, CPMVEC_SMC1,   0,    0 },    /* SMC1 ttyS0 */
+#ifdef CONFIG_8xx_SMC1
+  	{ 0,     0, PROFF_SMC1, CPMVEC_SMC1,   0,    QUICC_CPM_SMC1 },    /* SMC1 ttySx */
+#endif
+#ifdef CONFIG_8xx_SMC2
+  	{ 0,     0, PROFF_SMC2, CPMVEC_SMC2,   0,    QUICC_CPM_SMC2 },    /* SMC2 ttySx */
+#endif
+#ifdef CONFIG_8xx_SCC1
+  	{ 0,     0, PROFF_SCC1, CPMVEC_SCC1,   0,    QUICC_CPM_SCC1 },    /* SCC1 ttySx */
+#endif
+#ifdef CONFIG_8xx_SCC2
+  	{ 0,     0, PROFF_SCC2, CPMVEC_SCC2,   0,    QUICC_CPM_SCC2 },    /* SCC2 ttySx */
+#endif
+#ifdef CONFIG_8xx_SCC3
+  	{ 0,     0, PROFF_SCC3, CPMVEC_SCC3,   0,    QUICC_CPM_SCC3 },    /* SCC3 ttySx */
+#endif
+#ifdef CONFIG_8xx_SCC4
+  	{ 0,     0, PROFF_SCC4, CPMVEC_SCC4,   0,    QUICC_CPM_SCC4 },    /* SCC4 ttySx */
 #endif
-#if !defined(CONFIG_USB_MPC8xx) && !defined(CONFIG_USB_CLIENT_MPC8xx)
-# ifdef CONFIG_SMC2_UART
-  	{ 0,     0, PROFF_SMC2, CPMVEC_SMC2,   0,    1 },    /* SMC2 ttyS1 */
-# endif
-# ifdef CONFIG_USE_SCC_IO
-  	{ 0,     0, PROFF_SCC2, CPMVEC_SCC2,   0,    (NUM_IS_SCC | 1) },    /* SCC2 ttyS2 */
-  	{ 0,     0, PROFF_SCC3, CPMVEC_SCC3,   0,    (NUM_IS_SCC | 2) },    /* SCC3 ttyS3 */
-# endif
-  #else /* CONFIG_USB_xxx */
-# ifdef CONFIG_USE_SCC_IO
-  	{ 0,     0, PROFF_SCC3, CPMVEC_SCC3,   0,    (NUM_IS_SCC | 2) },    /* SCC3 ttyS3 */
-# endif
-#endif	/* CONFIG_USB_xxx */
 };
 
 #define NR_PORTS	(sizeof(rs_table)/sizeof(struct serial_state))
@@ -218,6 +237,12 @@
 	cbd_t			*rx_cur;
 	cbd_t			*tx_bd_base;
 	cbd_t			*tx_cur;
+
+	/* Virtual addresses for the FIFOs because we can't __va() a
+	 * physical address anymore.
+	 */
+	 unsigned char		*rx_va_base;
+	 unsigned char		*tx_va_base;
 } ser_info_t;
 
 static struct console sercons = {
@@ -394,13 +419,13 @@
 		/* Get the number of characters and the buffer pointer.
 		*/
 		i = bdp->cbd_datlen;
-		cp = (unsigned char *)__va(bdp->cbd_bufaddr);
+		cp = info->rx_va_base + ((bdp - info->rx_bd_base) * RX_BUF_SIZE);
 		status = bdp->cbd_sc;
 
 #ifdef CONFIG_KGDB
-		if (info->state->smc_scc_num == KGDB_SER_IDX) {
-			if (*cp == 0x03 || *cp == '$')
-				breakpoint();
+		if (info->state->smc_scc_num == KGDB_SER_IDX &&
+				(*cp == 0x03 || *cp == '$')) {
+			breakpoint();
 			return;
 		}
 #endif
@@ -420,7 +445,7 @@
 			icount->rx++;
 
 #ifdef SERIAL_DEBUG_INTR
-			printk("DR%02x:%02x...", ch, status);
+                        printk("DR%02x:%02x...", ch, status);
 #endif
 			*tty->flip.flag_buf_ptr = 0;
 			if (status & (BD_SC_BR | BD_SC_FR |
@@ -1033,7 +1058,7 @@
 			smcp->smc_smcmr = new_mode;
 	}
 
-	m8xx_cpm_setbrg((state - rs_table), baud_rate);
+	m8xx_cpm_setbrg(PORT_BRG(state->smc_scc_num), baud_rate);
 
 	restore_flags(flags);
 }
@@ -1042,6 +1067,7 @@
 {
 	ser_info_t *info = (ser_info_t *)tty->driver_data;
 	volatile cbd_t	*bdp;
+	unsigned char *cp;
 
 	if (serial_paranoia_check(info, tty->device, "rs_put_char"))
 		return;
@@ -1052,7 +1078,8 @@
 	bdp = info->tx_cur;
 	while (bdp->cbd_sc & BD_SC_READY);
 
-	*((char *)__va(bdp->cbd_bufaddr)) = ch;
+	cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE);
+	*cp = ch;
 	bdp->cbd_datlen = 1;
 	bdp->cbd_sc |= BD_SC_READY;
 
@@ -1064,7 +1091,6 @@
 		bdp++;
 
 	info->tx_cur = (cbd_t *)bdp;
-
 }
 
 static int rs_8xx_write(struct tty_struct * tty, int from_user,
@@ -1073,6 +1099,7 @@
 	int	c, ret = 0;
 	ser_info_t *info = (ser_info_t *)tty->driver_data;
 	volatile cbd_t *bdp;
+	unsigned char	*cp;
 
 #ifdef CONFIG_KGDB_CONSOLE
         /* Try to let stub handle output. Returns true if it did. */
@@ -1099,14 +1126,15 @@
 			break;
 		}
 
+		cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE);
 		if (from_user) {
-			if (copy_from_user(__va(bdp->cbd_bufaddr), buf, c)) {
+			if (copy_from_user((void *)cp, buf, c)) {
 				if (!ret)
 					ret = -EFAULT;
 				break;
 			}
 		} else {
-			memcpy(__va(bdp->cbd_bufaddr), buf, c);
+			memcpy((void *)cp, buf, c);
 		}
 
 		bdp->cbd_datlen = c;
@@ -1181,6 +1209,7 @@
 static void rs_8xx_send_xchar(struct tty_struct *tty, char ch)
 {
 	volatile cbd_t	*bdp;
+	unsigned char	*cp;
 
 	ser_info_t *info = (ser_info_t *)tty->driver_data;
 
@@ -1190,7 +1219,8 @@
 	bdp = info->tx_cur;
 	while (bdp->cbd_sc & BD_SC_READY);
 
-	*((char *)__va(bdp->cbd_bufaddr)) = ch;
+	cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE);
+	*cp = ch;
 	bdp->cbd_datlen = 1;
 	bdp->cbd_sc |= BD_SC_READY;
 
@@ -2217,6 +2247,11 @@
 
 #ifdef CONFIG_SERIAL_CONSOLE
 
+/* I need this just so I can store the virtual addresses and have
+ * common functions for the early console printing.
+ */
+static ser_info_t consinfo;
+
 /*
  * Print a string to the serial port trying not to disturb any possible
  * real use of the port...
@@ -2249,6 +2284,8 @@
 		/* Get the address of the host memory buffer.
 		 */
 		bdp = bdbase = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase];
+		
+		info = &consinfo;
 	}
 
 	/*
@@ -2276,7 +2313,7 @@
 		if ((uint)(bdp->cbd_bufaddr) > (uint)IMAP_ADDR)
 			cp = (u_char *)(bdp->cbd_bufaddr);
 		else
-			cp = __va(bdp->cbd_bufaddr);
+			cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE);
 		*cp = *s;
 
 		bdp->cbd_datlen = 1;
@@ -2290,7 +2327,18 @@
 		/* if a LF, also do CR... */
 		if (*s == 10) {
 			while (bdp->cbd_sc & BD_SC_READY);
-			cp = __va(bdp->cbd_bufaddr);
+			/* This 'if' below will never be true, but a few
+			 * people argued with me that it was a "bug by
+			 * inspection" that is was easier to add the code
+			 * than continue the discussion.  The only time
+			 * the buffer address is in DPRAM is during early
+			 * use by kgdb/xmon, which format their own packets
+			 * and we never get here. -- Dan
+			 */
+			if ((uint)(bdp->cbd_bufaddr) > (uint)IMAP_ADDR)
+				cp = (u_char *)(bdp->cbd_bufaddr);
+			else
+				cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE);
 			*cp = 13;
 			bdp->cbd_datlen = 1;
 			bdp->cbd_sc |= BD_SC_READY;
@@ -2358,10 +2406,13 @@
 	 * If the port has been initialized for general use, we must
 	 * use information from the port structure.
 	 */
-	if ((info = (ser_info_t *)ser->info))
+	if ((info = (ser_info_t *)ser->info)) {
 		bdp = info->rx_cur;
-	else
+	}
+	else {
 		bdp = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase];
+		info = &consinfo;
+	}
 
 	/*
 	 * We need to gracefully shut down the receiver, disable
@@ -2383,7 +2434,7 @@
 	if ((uint)(bdp->cbd_bufaddr) > (uint)IMAP_ADDR)
 		cp = (u_char *)(bdp->cbd_bufaddr);
 	else
-		cp = __va(bdp->cbd_bufaddr);
+		cp = info->rx_va_base + ((bdp - info->rx_bd_base) * RX_BUF_SIZE);
 
 	if (obuf) {
 		i = c = bdp->cbd_datlen;
@@ -2522,6 +2573,43 @@
 /*
  * The serial driver boot-time initialization code!
  */
+
+int __init rs_8xx_alloc_brg(int port)
+{
+    static int brg = 0;
+    volatile cpm8xx_t *cp = cpmp;
+    int res = brg;
+
+    /* "Wire" the BRG to the specified port
+    */
+    switch (port) {
+    case QUICC_CPM_SMC1:
+        cp->cp_simode = (cp->cp_simode & ~(0x07<<12)) | (brg<<12);
+        break;
+    case QUICC_CPM_SMC2:
+        cp->cp_simode = (cp->cp_simode & ~(0x07<<28)) | (brg<<28);
+        break;
+    case QUICC_CPM_SCC1:
+        cp->cp_sicr = (cp->cp_sicr & ~(0xFF<<0)) | (((brg<<3)|(brg<<0))<<0);
+        break;
+    case QUICC_CPM_SCC2:
+        cp->cp_sicr = (cp->cp_sicr & ~(0xFF<<8)) | (((brg<<3)|(brg<<0))<<8);
+        break;
+    case QUICC_CPM_SCC3:
+        cp->cp_sicr = (cp->cp_sicr & ~(0xFF<<16)) | (((brg<<3)|(brg<<0))<<16);
+        break;
+    case QUICC_CPM_SCC4:
+        cp->cp_sicr = (cp->cp_sicr & ~(0xFF<<24)) | (((brg<<3)|(brg<<0))<<24);
+        break;
+    }
+    /* Consume this BRG - Note: the last BRG will be reused if this
+    */
+    /* function is called too many times!
+    */
+    if (brg < QUICC_MAX_BRG) brg++;
+    return res;
+}
+
 int __init rs_8xx_init(void)
 {
 	struct serial_state * state;
@@ -2668,9 +2756,12 @@
 		state->icount.rx = state->icount.tx = 0;
 		state->icount.frame = state->icount.parity = 0;
 		state->icount.overrun = state->icount.brk = 0;
-		printk(KERN_INFO "ttyS%02d at 0x%04x is a %s\n",
+                PORT_BRG_SET(state->smc_scc_num, rs_8xx_alloc_brg(state->smc_scc_num));
+		printk(KERN_INFO "ttyS%d at 0x%04x is on %s%d using BRG%d\n",
 		       i, (unsigned int)(state->port),
-		       PORT_IS_SCC(state->smc_scc_num) ? "SCC" : "SMC");
+		       PORT_IS_SCC(state->smc_scc_num) ? "SCC" : "SMC",
+                       PORT_NUM(state->smc_scc_num)+1,
+                       PORT_BRG(state->smc_scc_num)+1);
 #ifdef CONFIG_SERIAL_CONSOLE
 		/* If we just printed the message on the console port, and
 		 * we are about to initialize it for general use, we have
@@ -2704,6 +2795,7 @@
 			/* Allocate space for FIFOs in the host memory.
 			*/
 			mem_addr = m8xx_cpm_hostalloc(RX_NUM_FIFO * RX_BUF_SIZE);
+			info->rx_va_base = (unsigned char *)mem_addr;
 
 			/* Set the physical address of the host memory
 			 * buffers in the buffer descriptors, and the
@@ -2713,12 +2805,12 @@
 			info->rx_cur = info->rx_bd_base = (cbd_t *)bdp;
 
 			for (j=0; j<(RX_NUM_FIFO-1); j++) {
-				bdp->cbd_bufaddr = __pa(mem_addr);
+				bdp->cbd_bufaddr = iopa(mem_addr);
 				bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT;
 				mem_addr += RX_BUF_SIZE;
 				bdp++;
 			}
-			bdp->cbd_bufaddr = __pa(mem_addr);
+			bdp->cbd_bufaddr = iopa(mem_addr);
 			bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT;
 
 			idx = PORT_NUM(info->state->smc_scc_num);
@@ -2738,6 +2830,7 @@
 			/* Allocate space for FIFOs in the host memory.
 			*/
 			mem_addr = m8xx_cpm_hostalloc(TX_NUM_FIFO * TX_BUF_SIZE);
+			info->tx_va_base = (unsigned char *)mem_addr;
 
 			/* Set the physical address of the host memory
 			 * buffers in the buffer descriptors, and the
@@ -2747,12 +2840,12 @@
 			info->tx_cur = info->tx_bd_base = (cbd_t *)bdp;
 
 			for (j=0; j<(TX_NUM_FIFO-1); j++) {
-				bdp->cbd_bufaddr = __pa(mem_addr);
+				bdp->cbd_bufaddr = iopa(mem_addr);
 				bdp->cbd_sc = BD_SC_INTRPT;
 				mem_addr += TX_BUF_SIZE;
 				bdp++;
 			}
-			bdp->cbd_bufaddr = __pa(mem_addr);
+			bdp->cbd_bufaddr = iopa(mem_addr);
 			bdp->cbd_sc = (BD_SC_WRAP | BD_SC_INTRPT);
 
 			if (PORT_IS_SCC(info->state->smc_scc_num)) {
@@ -2846,12 +2939,14 @@
 				}
 #endif /* CONFIG_ALTSMC2 */
 
+#if 0
 				/* Connect the baud rate generator to the
 				 * SMC based upon index in rs_table.  Also
 				 * make sure it is connected to NMSI.
 				 */
 				cp->cp_simode &= ~(0xffff << (idx * 16));
 				cp->cp_simode |= (i << ((idx * 16) + 12));
+#endif
 
 				up->smc_tbase = dp_addr;
 
@@ -2903,7 +2998,7 @@
 
 			/* Set up the baud rate generator.
 			*/
-			m8xx_cpm_setbrg(i, baud_table[baud_idx]);
+                        m8xx_cpm_setbrg(PORT_BRG(state->smc_scc_num), baud_table[baud_idx]);
 
 		}
 	}
@@ -2959,20 +3054,28 @@
 	 * from dual port ram, and a character buffer area from host mem.
 	 */
 
+	/* Allocate space for two FIFOs.  We can't allocate from host
+	 * memory yet because vm allocator isn't initialized
+	 * during this early console init.
+	 */
+	dp_addr = m8xx_cpm_dpalloc(8);
+	mem_addr = (uint)(&cpmp->cp_dpmem[dp_addr]);
+
 	/* Allocate space for two buffer descriptors in the DP ram.
 	*/
 	dp_addr = m8xx_cpm_dpalloc(sizeof(cbd_t) * 2);
 
-	/* Allocate space for two 2 byte FIFOs in the host memory.
-	*/
-	mem_addr = m8xx_cpm_hostalloc(8);
-
 	/* Set the physical address of the host memory buffers in
 	 * the buffer descriptors.
 	 */
 	bdp = (cbd_t *)&cp->cp_dpmem[dp_addr];
-	bdp->cbd_bufaddr = __pa(mem_addr);
-	(bdp+1)->cbd_bufaddr = __pa(mem_addr+4);
+	bdp->cbd_bufaddr = iopa(mem_addr);
+	(bdp+1)->cbd_bufaddr = iopa(mem_addr+4);
+
+	consinfo.rx_va_base = mem_addr;
+	consinfo.rx_bd_base = bdp;
+	consinfo.tx_va_base = mem_addr + 4;
+	consinfo.tx_bd_base = bdp+1;
 
 	/* For the receive, set empty and wrap.
 	 * For transmit, set wrap.
diff -urN linux-2.4.23-pre5/arch/ppc/Makefile linux-2.4.23-pre6/arch/ppc/Makefile
--- linux-2.4.23-pre5/arch/ppc/Makefile	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/Makefile	2003-10-01 10:38:44.000000000 -0700
@@ -14,7 +14,7 @@
 #
 
 # Be sure to change PAGE_OFFSET in include/asm-ppc/page.h to match
-KERNELLOAD	=0xc0000000
+KERNELLOAD	=$(CONFIG_KERNEL_START)
 
 LINKFLAGS	= -T arch/ppc/vmlinux.lds -Ttext $(KERNELLOAD) -Bstatic
 CPPFLAGS	:= $(CPPFLAGS) -I$(TOPDIR)/arch/$(ARCH)
diff -urN linux-2.4.23-pre5/arch/ppc/boot/simple/embed_config.c linux-2.4.23-pre6/arch/ppc/boot/simple/embed_config.c
--- linux-2.4.23-pre5/arch/ppc/boot/simple/embed_config.c	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/boot/simple/embed_config.c	2003-10-01 10:38:44.000000000 -0700
@@ -753,4 +753,3 @@
 	timebase_period_ns = 1000000000 / bd->bi_tbfreq;
 }
 #endif
-
diff -urN linux-2.4.23-pre5/arch/ppc/boot/simple/misc-embedded.c linux-2.4.23-pre6/arch/ppc/boot/simple/misc-embedded.c
--- linux-2.4.23-pre5/arch/ppc/boot/simple/misc-embedded.c	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/boot/simple/misc-embedded.c	2003-10-01 10:38:44.000000000 -0700
@@ -79,6 +79,14 @@
 extern void gunzip(void *, int, unsigned char *, int *);
 extern void embed_config(bd_t **bp);
 
+/* Weak function for boards which don't need to build the
+ * board info struct because they are using PPCBoot/U-Boot.
+ */
+void __attribute__ ((weak))
+embed_config(bd_t **bdp)
+{
+}
+
 unsigned long
 load_kernel(unsigned long load_addr, int num_words, unsigned long cksum,
 		bd_t *bp)
diff -urN linux-2.4.23-pre5/arch/ppc/config.in linux-2.4.23-pre6/arch/ppc/config.in
--- linux-2.4.23-pre5/arch/ppc/config.in	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/config.in	2003-10-01 10:38:44.000000000 -0700
@@ -12,6 +12,7 @@
 mainmenu_option next_comment
 comment 'Code maturity level options'
 bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
+bool 'Prompt for advanced kernel configuration options' CONFIG_ADVANCED_OPTIONS
 endmenu
 
 mainmenu_option next_comment
@@ -80,6 +81,7 @@
 	 RPX-Classic	CONFIG_RPXCLASSIC	\
 	 BSE-IP		CONFIG_BSEIP		\
 	 FADS		CONFIG_FADS		\
+	 D-Box2		CONFIG_DBOX2		\
 	 TQM823L	CONFIG_TQM823L		\
 	 TQM850L	CONFIG_TQM850L		\
 	 TQM855L	CONFIG_TQM855L		\
@@ -248,8 +250,49 @@
 comment 'General setup'
 
 bool 'High memory support (experimental)' CONFIG_HIGHMEM
+if [ "$CONFIG_ADVANCED_OPTIONS" = "y" ]; then
+  if [ "$CONFIG_HIGHMEM" = "y" ]; then
+    bool "  Set high memory pool address" CONFIG_HIGHMEM_START_BOOL
+    if [ "$CONFIG_HIGHMEM_START_BOOL" = "y" ]; then
+      hex "    Virtual start address of high memory pool" CONFIG_HIGHMEM_START 0xfe000000
+    fi
+  fi
+  bool "  Set maximum low memory" CONFIG_LOWMEM_SIZE_BOOL
+  if [ "$CONFIG_LOWMEM_SIZE_BOOL" = "y" ]; then
+    hex "    Maximum low memory size (in bytes)" CONFIG_LOWMEM_SIZE 0x20000000
+  fi
+
+  bool "Set custom kernel base address" CONFIG_KERNEL_START_BOOL
+  if [ "$CONFIG_KERNEL_START_BOOL" = "y" ]; then
+    hex "  Virtual address of kernel base" CONFIG_KERNEL_START 0xc0000000
+  fi
+  bool "Set custom user task size" CONFIG_TASK_SIZE_BOOL
+  if [ "$CONFIG_TASK_SIZE_BOOL" = "y" ]; then
+    hex "  Size of user task space" CONFIG_TASK_SIZE 0x80000000
+  fi
+fi
+
+if [ "$CONFIG_HIGHMEM_START_BOOL" != "y" ]; then
+  define_hex CONFIG_HIGHMEM_START 0xfe000000
+fi
 
-define_bool CONFIG_ISA n
+if [ "$CONFIG_LOWMEM_SIZE_BOOL" != "y" ]; then
+  define_hex CONFIG_LOWMEM_SIZE 0x30000000
+fi
+
+if [ "$CONFIG_KERNEL_START_BOOL" != "y" ]; then
+  define_hex CONFIG_KERNEL_START 0xc0000000
+fi
+
+if [ "$CONFIG_TASK_SIZE_BOOL" != "y" ]; then
+  define_hex CONFIG_TASK_SIZE 0x80000000
+fi
+
+if [ "$CONFIG_ALL_PPC" = "y" ]; then
+  bool 'Support for ISA-bus hardware' CONFIG_ISA
+else
+  define_bool CONFIG_ISA n
+fi
 define_bool CONFIG_EISA n
 define_bool CONFIG_SBUS n
 
diff -urN linux-2.4.23-pre5/arch/ppc/configs/IVMS8_defconfig linux-2.4.23-pre6/arch/ppc/configs/IVMS8_defconfig
--- linux-2.4.23-pre5/arch/ppc/configs/IVMS8_defconfig	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/configs/IVMS8_defconfig	2003-10-01 10:38:44.000000000 -0700
@@ -506,14 +506,8 @@
 # CONFIG_SOUND is not set
 
 #
-# MPC8xx CPM Options
+# MPC8xx Options
 #
-# CONFIG_SCC_ENET is not set
-CONFIG_FEC_ENET=y
-CONFIG_USE_MDIO=y
-CONFIG_ENET_BIG_BUFFERS=y
-# CONFIG_SMC2_UART is not set
-# CONFIG_USE_SCC_IO is not set
 
 #
 # Generic MPC8xx Options
@@ -523,6 +517,27 @@
 # CONFIG_UCODE_PATCH is not set
 
 #
+# MPC8xx CPM Options
+#
+# CONFIG_SCC_ENET is not set
+CONFIG_FEC_ENET=y
+CONFIG_USE_MDIO=y
+# CONFIG_FEC_GENERIC_PHY is not set
+# CONFIG_FEC_AM79C874 is not set
+# CONFIG_FEC_LXT970 is not set
+# CONFIG_FEC_QS6612 is not set
+# CONFIG_FEC_LXT970 is not set
+CONFIG_FEC_DP83843=y
+# CONFIG_FEC_DP83846A is not set
+CONFIG_ENET_BIG_BUFFERS=y
+CONFIG_8xx_SMC1=y
+# CONFIG_SMC2_UART is not set
+# CONFIG_8xx_SCC1 is not set
+# CONFIG_8xx_SCC2 is not set
+# CONFIG_8xx_SCC3 is not set
+# CONFIG_8xx_SCC4 is not set
+
+#
 # USB support
 #
 # CONFIG_USB is not set
diff -urN linux-2.4.23-pre5/arch/ppc/configs/SM850_defconfig linux-2.4.23-pre6/arch/ppc/configs/SM850_defconfig
--- linux-2.4.23-pre5/arch/ppc/configs/SM850_defconfig	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/configs/SM850_defconfig	2003-10-01 10:38:44.000000000 -0700
@@ -470,6 +470,17 @@
 # CONFIG_SOUND is not set
 
 #
+# MPC8xx Options
+#
+
+#
+# Generic MPC8xx Options
+#
+CONFIG_8xx_COPYBACK=y
+CONFIG_8xx_CPU6=y
+# CONFIG_UCODE_PATCH is not set
+
+#
 # MPC8xx CPM Options
 #
 CONFIG_SCC_ENET=y
@@ -481,14 +492,9 @@
 CONFIG_SMC2_UART=y
 CONFIG_ALTSMC2=y
 CONFIG_CONS_SMC2=y
-# CONFIG_USE_SCC_IO is not set
-
-#
-# Generic MPC8xx Options
-#
-CONFIG_8xx_COPYBACK=y
-CONFIG_8xx_CPU6=y
-# CONFIG_UCODE_PATCH is not set
+# CONFIG_8xx_SCC1 is not set
+# CONFIG_8xx_SCC2 is not set
+# CONFIG_8xx_SCC4 is not set
 
 #
 # USB support
diff -urN linux-2.4.23-pre5/arch/ppc/configs/SPD823TS_defconfig linux-2.4.23-pre6/arch/ppc/configs/SPD823TS_defconfig
--- linux-2.4.23-pre5/arch/ppc/configs/SPD823TS_defconfig	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/configs/SPD823TS_defconfig	2003-10-01 10:38:44.000000000 -0700
@@ -469,6 +469,17 @@
 # CONFIG_SOUND is not set
 
 #
+# MPC8xx Options
+#
+
+#
+# Generic MPC8xx Options
+#
+CONFIG_8xx_COPYBACK=y
+# CONFIG_8xx_CPU6 is not set
+# CONFIG_UCODE_PATCH is not set
+
+#
 # MPC8xx CPM Options
 #
 CONFIG_SCC_ENET=y
@@ -477,17 +488,13 @@
 # CONFIG_SCC3_ENET is not set
 # CONFIG_FEC_ENET is not set
 CONFIG_ENET_BIG_BUFFERS=y
+CONFIG_8xx_SMC1=y
 CONFIG_SMC2_UART=y
 CONFIG_ALTSMC2=y
 # CONFIG_CONS_SMC2 is not set
-# CONFIG_USE_SCC_IO is not set
-
-#
-# Generic MPC8xx Options
-#
-CONFIG_8xx_COPYBACK=y
-# CONFIG_8xx_CPU6 is not set
-# CONFIG_UCODE_PATCH is not set
+# CONFIG_8xx_SCC1 is not set
+# CONFIG_8xx_SCC3 is not set
+# CONFIG_8xx_SCC4 is not set
 
 #
 # USB support
diff -urN linux-2.4.23-pre5/arch/ppc/configs/TQM823L_defconfig linux-2.4.23-pre6/arch/ppc/configs/TQM823L_defconfig
--- linux-2.4.23-pre5/arch/ppc/configs/TQM823L_defconfig	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/configs/TQM823L_defconfig	2003-10-01 10:38:44.000000000 -0700
@@ -470,6 +470,17 @@
 # CONFIG_SOUND is not set
 
 #
+# MPC8xx Options
+#
+
+#
+# Generic MPC8xx Options
+#
+CONFIG_8xx_COPYBACK=y
+# CONFIG_8xx_CPU6 is not set
+# CONFIG_UCODE_PATCH is not set
+
+#
 # MPC8xx CPM Options
 #
 CONFIG_SCC_ENET=y
@@ -478,17 +489,13 @@
 # CONFIG_SCC3_ENET is not set
 # CONFIG_FEC_ENET is not set
 CONFIG_ENET_BIG_BUFFERS=y
+CONFIG_8xx_SMC1=y
 CONFIG_SMC2_UART=y
 CONFIG_ALTSMC2=y
 # CONFIG_CONS_SMC2 is not set
-# CONFIG_USE_SCC_IO is not set
-
-#
-# Generic MPC8xx Options
-#
-CONFIG_8xx_COPYBACK=y
-# CONFIG_8xx_CPU6 is not set
-# CONFIG_UCODE_PATCH is not set
+# CONFIG_8xx_SCC1 is not set
+# CONFIG_8xx_SCC3 is not set
+# CONFIG_8xx_SCC4 is not set
 
 #
 # USB support
diff -urN linux-2.4.23-pre5/arch/ppc/configs/TQM850L_defconfig linux-2.4.23-pre6/arch/ppc/configs/TQM850L_defconfig
--- linux-2.4.23-pre5/arch/ppc/configs/TQM850L_defconfig	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/configs/TQM850L_defconfig	2003-10-01 10:38:44.000000000 -0700
@@ -470,6 +470,17 @@
 # CONFIG_SOUND is not set
 
 #
+# MPC8xx Options
+#
+
+#
+# Generic MPC8xx Options
+#
+CONFIG_8xx_COPYBACK=y
+# CONFIG_8xx_CPU6 is not set
+# CONFIG_UCODE_PATCH is not set
+
+#
 # MPC8xx CPM Options
 #
 CONFIG_SCC_ENET=y
diff -urN linux-2.4.23-pre5/arch/ppc/configs/TQM860L_defconfig linux-2.4.23-pre6/arch/ppc/configs/TQM860L_defconfig
--- linux-2.4.23-pre5/arch/ppc/configs/TQM860L_defconfig	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/configs/TQM860L_defconfig	2003-10-01 10:38:44.000000000 -0700
@@ -511,6 +511,17 @@
 # CONFIG_SOUND is not set
 
 #
+# MPC8xx Options
+#
+
+#
+# Generic MPC8xx Options
+#
+CONFIG_8xx_COPYBACK=y
+# CONFIG_8xx_CPU6 is not set
+# CONFIG_UCODE_PATCH is not set
+
+#
 # MPC8xx CPM Options
 #
 CONFIG_SCC_ENET=y
@@ -519,17 +530,13 @@
 # CONFIG_SCC3_ENET is not set
 # CONFIG_FEC_ENET is not set
 CONFIG_ENET_BIG_BUFFERS=y
+CONFIG_8xx_SMC1=y
 CONFIG_SMC2_UART=y
 # CONFIG_ALTSMC2 is not set
 # CONFIG_CONS_SMC2 is not set
-# CONFIG_USE_SCC_IO is not set
-
-#
-# Generic MPC8xx Options
-#
-CONFIG_8xx_COPYBACK=y
-# CONFIG_8xx_CPU6 is not set
-# CONFIG_UCODE_PATCH is not set
+# CONFIG_8xx_SCC2 is not set
+# CONFIG_8xx_SCC3 is not set
+# CONFIG_8xx_SCC4 is not set
 
 #
 # USB support
diff -urN linux-2.4.23-pre5/arch/ppc/configs/bseip_defconfig linux-2.4.23-pre6/arch/ppc/configs/bseip_defconfig
--- linux-2.4.23-pre5/arch/ppc/configs/bseip_defconfig	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/configs/bseip_defconfig	2003-10-01 10:38:44.000000000 -0700
@@ -469,6 +469,17 @@
 # CONFIG_SOUND is not set
 
 #
+# MPC8xx Options
+#
+
+#
+# Generic MPC8xx Options
+#
+CONFIG_8xx_COPYBACK=y
+# CONFIG_8xx_CPU6 is not set
+# CONFIG_UCODE_PATCH is not set
+
+#
 # MPC8xx CPM Options
 #
 CONFIG_SCC_ENET=y
@@ -477,17 +488,13 @@
 # CONFIG_SCC3_ENET is not set
 # CONFIG_FEC_ENET is not set
 # CONFIG_ENET_BIG_BUFFERS is not set
+CONFIG_8xx_SMC1=y
 CONFIG_SMC2_UART=y
 # CONFIG_ALTSMC2 is not set
 # CONFIG_CONS_SMC2 is not set
-# CONFIG_USE_SCC_IO is not set
-
-#
-# Generic MPC8xx Options
-#
-CONFIG_8xx_COPYBACK=y
-# CONFIG_8xx_CPU6 is not set
-# CONFIG_UCODE_PATCH is not set
+# CONFIG_8xx_SCC1 is not set
+# CONFIG_8xx_SCC3 is not set
+# CONFIG_8xx_SCC4 is not set
 
 #
 # USB support
diff -urN linux-2.4.23-pre5/arch/ppc/configs/mbx_defconfig linux-2.4.23-pre6/arch/ppc/configs/mbx_defconfig
--- linux-2.4.23-pre5/arch/ppc/configs/mbx_defconfig	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/configs/mbx_defconfig	2003-10-01 10:38:44.000000000 -0700
@@ -465,6 +465,17 @@
 # CONFIG_SOUND is not set
 
 #
+# MPC8xx Options
+#
+
+#
+# Generic MPC8xx Options
+#
+CONFIG_8xx_COPYBACK=y
+CONFIG_8xx_CPU6=y
+# CONFIG_UCODE_PATCH is not set
+
+#
 # MPC8xx CPM Options
 #
 CONFIG_SCC_ENET=y
@@ -473,17 +484,13 @@
 # CONFIG_SCC3_ENET is not set
 # CONFIG_FEC_ENET is not set
 CONFIG_ENET_BIG_BUFFERS=y
+CONFIG_8xx_SMC1=y
 CONFIG_SMC2_UART=y
 # CONFIG_ALTSMC2 is not set
 # CONFIG_CONS_SMC2 is not set
-CONFIG_USE_SCC_IO=y
-
-#
-# Generic MPC8xx Options
-#
-CONFIG_8xx_COPYBACK=y
-CONFIG_8xx_CPU6=y
-# CONFIG_UCODE_PATCH is not set
+# CONFIG_8xx_SCC2 is not set
+# CONFIG_8xx_SCC3 is not set
+# CONFIG_8xx_SCC4 is not set
 
 #
 # USB support
diff -urN linux-2.4.23-pre5/arch/ppc/configs/rpxcllf_defconfig linux-2.4.23-pre6/arch/ppc/configs/rpxcllf_defconfig
--- linux-2.4.23-pre5/arch/ppc/configs/rpxcllf_defconfig	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/configs/rpxcllf_defconfig	2003-10-01 10:38:44.000000000 -0700
@@ -504,6 +504,17 @@
 # CONFIG_SOUND is not set
 
 #
+# MPC8xx Options
+#
+
+#
+# Generic MPC8xx Options
+#
+CONFIG_8xx_COPYBACK=y
+# CONFIG_8xx_CPU6 is not set
+# CONFIG_UCODE_PATCH is not set
+
+#
 # MPC8xx CPM Options
 #
 CONFIG_SCC_ENET=y
@@ -513,17 +524,13 @@
 CONFIG_FEC_ENET=y
 # CONFIG_USE_MDIO is not set
 CONFIG_ENET_BIG_BUFFERS=y
+CONFIG_8xx_SMC1=y
 CONFIG_SMC2_UART=y
 # CONFIG_ALTSMC2 is not set
 # CONFIG_CONS_SMC2 is not set
-CONFIG_USE_SCC_IO=y
-
-#
-# Generic MPC8xx Options
-#
-CONFIG_8xx_COPYBACK=y
-# CONFIG_8xx_CPU6 is not set
-# CONFIG_UCODE_PATCH is not set
+# CONFIG_8xx_SCC2 is not set
+# CONFIG_8xx_SCC3 is not set
+# CONFIG_8xx_SCC4 is not set
 
 #
 # USB support
diff -urN linux-2.4.23-pre5/arch/ppc/configs/rpxlite_defconfig linux-2.4.23-pre6/arch/ppc/configs/rpxlite_defconfig
--- linux-2.4.23-pre5/arch/ppc/configs/rpxlite_defconfig	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/configs/rpxlite_defconfig	2003-10-01 10:38:44.000000000 -0700
@@ -504,6 +504,17 @@
 # CONFIG_SOUND is not set
 
 #
+# MPC8xx Options
+#
+
+#
+# Generic MPC8xx Options
+#
+CONFIG_8xx_COPYBACK=y
+# CONFIG_8xx_CPU6 is not set
+# CONFIG_UCODE_PATCH is not set
+
+#
 # MPC8xx CPM Options
 #
 CONFIG_SCC_ENET=y
@@ -512,15 +523,11 @@
 # CONFIG_SCC3_ENET is not set
 # CONFIG_FEC_ENET is not set
 # CONFIG_ENET_BIG_BUFFERS is not set
+CONFIG_8xx_SMC1=y
 # CONFIG_SMC2_UART is not set
-# CONFIG_USE_SCC_IO is not set
-
-#
-# Generic MPC8xx Options
-#
-CONFIG_8xx_COPYBACK=y
-# CONFIG_8xx_CPU6 is not set
-# CONFIG_UCODE_PATCH is not set
+# CONFIG_8xx_SCC1 is not set
+# CONFIG_8xx_SCC3 is not set
+# CONFIG_8xx_SCC4 is not set
 
 #
 # USB support
diff -urN linux-2.4.23-pre5/arch/ppc/kernel/head_8xx.S linux-2.4.23-pre6/arch/ppc/kernel/head_8xx.S
--- linux-2.4.23-pre5/arch/ppc/kernel/head_8xx.S	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/kernel/head_8xx.S	2003-10-01 10:38:44.000000000 -0700
@@ -21,9 +21,9 @@
  *
  */
 
+#include <linux/config.h>
 #include <asm/processor.h>
 #include <asm/page.h>
-#include <linux/config.h>
 #include <asm/mmu.h>
 #include <asm/cache.h>
 #include <asm/pgtable.h>
@@ -817,16 +817,31 @@
  * kernel initialization.  This maps the first 8 MBytes of memory 1:1
  * virtual to physical.  Also, set the cache mode since that is defined
  * by TLB entries and perform any additional mapping (like of the IMMR).
+ * If configured to pin some TLBs, we pin the first 8 Mbytes of kernel,
+ * 24 Mbytes of data, and the 8M IMMR space.  Anything not covered by
+ * these mappings is mapped by page tables.
  */
 initial_mmu:
 	tlbia			/* Invalidate all TLB entries */
+#ifdef CONFIG_PIN_TLB
+	lis	r8, MI_RSV4I@h
+	ori	r8, r8, 0x1c00
+#else
 	li	r8, 0
-	mtspr	MI_CTR, r8	/* Set instruction control to zero */
-	lis	r8, MD_RESETVAL@h
+#endif
+	mtspr	MI_CTR, r8	/* Set instruction MMU control */
+
+#ifdef CONFIG_PIN_TLB
+	lis	r10, (MD_RSV4I | MD_RESETVAL)@h
+	ori	r10, r10, 0x1c00
+	mr	r8, r10
+#else
+	lis	r10, MD_RESETVAL@h
+#endif
 #ifndef CONFIG_8xx_COPYBACK
-	oris	r8, r8, MD_WTDEF@h
+	oris	r10, r10, MD_WTDEF@h
 #endif
-	mtspr	MD_CTR, r8	/* Set data TLB control */
+	mtspr	MD_CTR, r10	/* Set data TLB control */
 
 	/* Now map the lower 8 Meg into the TLBs.  For this quick hack,
 	 * we can load the instruction and data TLB registers with the
@@ -850,6 +865,10 @@
 	/* Map another 8 MByte at the IMMR to get the processor
 	 * internal registers (among other things).
 	 */
+#ifdef CONFIG_PIN_TLB
+	addi	r10, r10, 0x0100
+	mtspr	MD_CTR, r10
+#endif
 	mfspr	r9, 638			/* Get current IMMR */
 	andis.	r9, r9, 0xff80		/* Get 8Mbyte boundary */
 
@@ -863,6 +882,30 @@
 	ori	r8, r8, MI_BOOTINIT|0x2 /* Inhibit cache -- Cort */
 	mtspr	MD_RPN, r8
 
+#ifdef CONFIG_PIN_TLB
+	/* Map two more 8M kernel data pages.
+	*/
+	addi	r10, r10, 0x0100
+	mtspr	MD_CTR, r10
+
+	lis	r8, KERNELBASE@h	/* Create vaddr for TLB */
+	addis	r8, r8, 0x0080		/* Add 8M */
+	ori	r8, r8, MI_EVALID	/* Mark it valid */
+	mtspr	MD_EPN, r8
+	li	r9, MI_PS8MEG		/* Set 8M byte page */
+	ori	r9, r9, MI_SVALID	/* Make it valid */
+	mtspr	MD_TWC, r9
+	li	r11, MI_BOOTINIT	/* Create RPN for address 0 */
+	addis	r11, r11, 0x0080	/* Add 8M */
+	mtspr	MD_RPN, r8
+
+	addis	r8, r8, 0x0080		/* Add 8M */
+	mtspr	MD_EPN, r8
+	mtspr	MD_TWC, r9
+	addis	r11, r11, 0x0080	/* Add 8M */
+	mtspr	MD_RPN, r8
+#endif
+
 	/* Since the cache is enabled according to the information we
 	 * just loaded into the TLB, invalidate and enable the caches here.
 	 * We should probably check/set other modes....later.
diff -urN linux-2.4.23-pre5/arch/ppc/kernel/indirect_pci.c linux-2.4.23-pre6/arch/ppc/kernel/indirect_pci.c
--- linux-2.4.23-pre5/arch/ppc/kernel/indirect_pci.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/kernel/indirect_pci.c	2003-10-01 10:38:44.000000000 -0700
@@ -24,6 +24,12 @@
 #define cfg_read(val, addr, type, op)	*val = op((type)(addr))
 #define cfg_write(val, addr, type, op)	op((type *)(addr), (val))
 
+#ifdef CONFIG_PPC_INDIRECT_PCI_BE
+#define PCI_CFG_OUT out_be32
+#else
+#define PCI_CFG_OUT out_le32
+#endif
+
 #define INDIRECT_PCI_OP(rw, size, type, op, mask)			 \
 static int								 \
 indirect_##rw##_config_##size(struct pci_dev *dev, int offset, type val) \
@@ -39,9 +45,9 @@
 		if (dev->bus->number != hose->first_busno)		 \
 			cfg_type = 1;					 \
 									 \
-	out_be32(hose->cfg_addr, 					 \
-		 (((offset & 0xfc) | cfg_type) << 24) | (dev->devfn << 16) \
-		 | ((dev->bus->number - hose->bus_offset) << 8) | 0x80); \
+	PCI_CFG_OUT(hose->cfg_addr, 					 \
+		 (0x80000000 | ((dev->bus->number - hose->bus_offset) << 16) \
+		  | (dev->devfn << 8) | ((offset & 0xfc) | cfg_type)));	 \
 	cfg_##rw(val, hose->cfg_data + (offset & mask), type, op);	 \
 	return PCIBIOS_SUCCESSFUL;    					 \
 }
diff -urN linux-2.4.23-pre5/arch/ppc/kernel/irq.c linux-2.4.23-pre6/arch/ppc/kernel/irq.c
--- linux-2.4.23-pre5/arch/ppc/kernel/irq.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/kernel/irq.c	2003-10-01 10:38:44.000000000 -0700
@@ -429,7 +429,7 @@
 	int status;
 	struct irqaction *action;
 	int cpu = smp_processor_id();
-	irq_desc_t *desc = irq_desc + irq;
+	irq_desc_t *desc = &irq_desc[irq];
 
 	kstat.irqs[cpu][irq]++;
 	spin_lock(&desc->lock);
@@ -501,12 +501,25 @@
 	 * The ->end() handler has to deal with interrupts which got
 	 * disabled while the handler was running.
 	 */
-	if (irq_desc[irq].handler) {
-		if (irq_desc[irq].handler->end)
-			irq_desc[irq].handler->end(irq);
-		else if (irq_desc[irq].handler->enable)
-			irq_desc[irq].handler->enable(irq);
+	if (desc->handler) {
+		if (desc->handler->end)
+			desc->handler->end(irq);
+		else if (desc->handler->enable)
+			desc->handler->enable(irq);
 	}
+
+#ifdef CONFIG_DBOX2
+	/*
+	 * Interrupts marked as oneshot are level
+	 * triggered. We disable them here for onboard
+	 * hardware which can not be configured to
+	 * generate edge triggered interrupts due to
+	 * lack of documentation.
+	 */
+	if ((action) && (action->flags & SA_ONESHOT))
+		disable_irq_nosync(irq);
+#endif
+
 	spin_unlock(&desc->lock);
 }
 
diff -urN linux-2.4.23-pre5/arch/ppc/kernel/ppc_ksyms.c linux-2.4.23-pre6/arch/ppc/kernel/ppc_ksyms.c
--- linux-2.4.23-pre5/arch/ppc/kernel/ppc_ksyms.c	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/kernel/ppc_ksyms.c	2003-10-01 10:38:44.000000000 -0700
@@ -162,7 +162,7 @@
 EXPORT_SYMBOL(iopa);
 EXPORT_SYMBOL(mm_ptov);
 EXPORT_SYMBOL(ioremap);
-#ifdef CONFIG_44x
+#ifdef CONFIG_PTE_64BIT
 EXPORT_SYMBOL(ioremap64);
 #endif
 EXPORT_SYMBOL(__ioremap);
@@ -356,6 +356,7 @@
 EXPORT_SYMBOL(cpm_install_handler);
 EXPORT_SYMBOL(cpm_free_handler);
 EXPORT_SYMBOL(m8xx_cpm_hostalloc);
+EXPORT_SYMBOL(m8xx_cpm_dpalloc);
 #endif /* CONFIG_8xx */
 
 /* Those should really be inline */
diff -urN linux-2.4.23-pre5/arch/ppc/mm/pgtable.c linux-2.4.23-pre6/arch/ppc/mm/pgtable.c
--- linux-2.4.23-pre5/arch/ppc/mm/pgtable.c	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/arch/ppc/mm/pgtable.c	2003-10-01 10:38:44.000000000 -0700
@@ -37,10 +37,11 @@
 unsigned long ioremap_bot;
 int io_bat_index;
 
-/* Maximum 768Mb of lowmem. On SMP, this value will be
- * trimmed down to whatever can be covered by BATs though.
+/* The maximum lowmem defaults to 768Mb, but this can be configured to
+ * another value.  On SMP, this value will be trimmed down to whatever
+ * can be covered by BATs.
  */
-#define MAX_LOW_MEM	0x30000000
+#define MAX_LOW_MEM	CONFIG_LOWMEM_SIZE
 
 #ifndef CONFIG_SMP
 struct pgtable_cache_struct quicklists;
@@ -67,7 +68,7 @@
 #define p_mapped_by_bats(x)	(0UL)
 #endif /* HAVE_BATS */
 
-#ifdef CONFIG_44x
+#ifdef CONFIG_PTE_64BIT
 void *
 ioremap(phys_addr_t addr, unsigned long size)
 {
@@ -82,14 +83,14 @@
 	return __ioremap(addr, size, _PAGE_NO_CACHE);
 }
 
-#else /* CONFIG_44x */
+#else /* !CONFIG_PTE_64BIT */
 
 void *
 ioremap(phys_addr_t addr, unsigned long size)
 {
 	return __ioremap(addr, size, _PAGE_NO_CACHE);
 }
-#endif /* CONFIG_44x */
+#endif /* CONFIG_PTE_64BIT */
 
 void *
 __ioremap(phys_addr_t addr, unsigned long size, unsigned long flags)
diff -urN linux-2.4.23-pre5/arch/ppc/platforms/dbox2.h linux-2.4.23-pre6/arch/ppc/platforms/dbox2.h
--- linux-2.4.23-pre5/arch/ppc/platforms/dbox2.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/arch/ppc/platforms/dbox2.h	2003-10-01 10:38:44.000000000 -0700
@@ -0,0 +1,24 @@
+/*
+ * D-BOX2 board specific definitions
+ * 
+ * Copyright (c) 2001-2002 Florian Schirmer (jolt@tuxbox.org)
+ */
+
+#ifndef __MACH_DBOX2_H
+#define __MACH_DBOX2_H
+
+#include <linux/config.h>
+ 
+#include <asm/ppcboot.h>
+
+#define	DBOX2_IMMR_BASE	0xFF000000	/* phys. addr of IMMR */
+#define	DBOX2_IMAP_SIZE	(64 * 1024)	/* size of mapped area */
+
+#define	IMAP_ADDR	DBOX2_IMMR_BASE	/* physical base address of IMMR area */
+#define IMAP_SIZE	DBOX2_IMAP_SIZE	/* mapped size of IMMR area */
+
+/* We don't use the 8259.
+*/
+#define NR_8259_INTS	0
+
+#endif	/* __MACH_DBOX2_H */
diff -urN linux-2.4.23-pre5/arch/sh/Makefile linux-2.4.23-pre6/arch/sh/Makefile
--- linux-2.4.23-pre5/arch/sh/Makefile	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/sh/Makefile	2003-10-01 10:38:45.000000000 -0700
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.1.1.1.2.2 2002/05/10 17:58:54 jzs Exp $
+# $Id: Makefile,v 1.1.1.1.2.4 2003/08/22 04:04:16 sugioka Exp $
 #
 # 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
@@ -87,7 +87,7 @@
 FORCE: ;
 
 zImage: vmlinux
-	@$(MAKEBOOT) zImage
+	@$(MAKEBOOT) $@
 
 compressed: zImage
 
diff -urN linux-2.4.23-pre5/arch/sh/boot/compressed/Makefile linux-2.4.23-pre6/arch/sh/boot/compressed/Makefile
--- linux-2.4.23-pre5/arch/sh/boot/compressed/Makefile	2000-10-02 11:57:33.000000000 -0700
+++ linux-2.4.23-pre6/arch/sh/boot/compressed/Makefile	2003-10-01 10:38:44.000000000 -0700
@@ -18,9 +18,9 @@
 #
 # ZIMAGE_OFFSET is the load offset of the compression loader
 #
-ZIMAGE_OFFSET = $(shell printf "0x%8x" $$[0x80000000+0x$(CONFIG_MEMORY_START)+0x200000+0x10000])
+ZIMAGE_OFFSET = $(shell printf "0x%8x" $$[0x80000000+0x$(CONFIG_MEMORY_START)+0x$(CONFIG_BOOT_LINK_OFFSET)])
 
-ZLINKFLAGS = -Ttext $(ZIMAGE_OFFSET) $(ZLDFLAGS)
+ZLINKFLAGS  = -Ttext $(ZIMAGE_OFFSET)  $(ZLDFLAGS)
 
 all: vmlinux
 
diff -urN linux-2.4.23-pre5/arch/sh/boot/compressed/head.S linux-2.4.23-pre6/arch/sh/boot/compressed/head.S
--- linux-2.4.23-pre5/arch/sh/boot/compressed/head.S	2001-01-28 18:56:00.000000000 -0800
+++ linux-2.4.23-pre6/arch/sh/boot/compressed/head.S	2003-10-01 10:38:44.000000000 -0700
@@ -2,6 +2,7 @@
  *  linux/arch/sh/boot/compressed/head.S
  *
  *  Copyright (C) 1999 Stuart Menefy
+ *  Copyright (C) 2003 SUGIOKA Toshinobu
  */
 
 .text
@@ -14,7 +15,54 @@
 	mov.l   init_sr, r1
 	ldc     r1, sr
 
-	/* First clear BSS */
+	/* Move myself to proper location if necessary */
+	mova	1f, r0
+	mov.l	1f, r2
+	cmp/eq	r2, r0
+	bt	clear_bss
+	sub	r0, r2
+	mov.l	bss_start_addr, r0
+	mov	#0xe0, r1
+	and	r1, r0			! align cache line
+	mov.l	text_start_addr, r3
+	mov	r0, r1
+	sub	r2, r1
+3:
+	mov.l	@r1, r4
+	mov.l	@(4,r1), r5
+	mov.l	@(8,r1), r6
+	mov.l	@(12,r1), r7
+	mov.l	@(16,r1), r8
+	mov.l	@(20,r1), r9
+	mov.l	@(24,r1), r10
+	mov.l	@(28,r1), r11
+	mov.l	r4, @r0
+	mov.l	r5, @(4,r0)
+	mov.l	r6, @(8,r0)
+	mov.l	r7, @(12,r0)
+	mov.l	r8, @(16,r0)
+	mov.l	r9, @(20,r0)
+	mov.l	r10, @(24,r0)
+	mov.l	r11, @(28,r0)
+#ifdef CONFIG_CPU_SH4
+	ocbwb	@r0
+#endif
+	cmp/hi	r3, r0
+	add	#-32, r0
+	bt/s	3b
+	 add	#-32, r1
+	mov.l	2f, r0
+	jmp	@r0
+	 nop
+
+	.align 2
+1:	.long	1b
+2:	.long	clear_bss
+text_start_addr:
+	.long	startup
+
+	/* Clear BSS */
+clear_bss:
 	mov.l	end_addr, r1
 	mov.l	bss_start_addr, r2
 	mov	#0, r0
diff -urN linux-2.4.23-pre5/arch/sh/config.in linux-2.4.23-pre6/arch/sh/config.in
--- linux-2.4.23-pre5/arch/sh/config.in	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/arch/sh/config.in	2003-10-01 10:38:44.000000000 -0700
@@ -28,6 +28,7 @@
 comment 'Processor type and features'
 choice 'SuperH system type'					\
 	"Generic		CONFIG_SH_GENERIC		\
+	 SH4-202-MicroDev	CONFIG_SH_SH4202_MICRODEV	\
 	 SolutionEngine		CONFIG_SH_SOLUTION_ENGINE	\
 	 SolutionEngine7751	CONFIG_SH_7751_SOLUTION_ENGINE	\
 	 SHMobileSolutionEngine	CONFIG_SH_MOBILE_SOLUTION_ENGINE \
@@ -68,7 +69,9 @@
     SH7708 CONFIG_CPU_SUBTYPE_SH7708 \
     SH7709 CONFIG_CPU_SUBTYPE_SH7709 \
     SH7750 CONFIG_CPU_SUBTYPE_SH7750 \
+    SH7751R CONFIG_CPU_SUBTYPE_SH7751R \
     SH7751 CONFIG_CPU_SUBTYPE_SH7751 \
+    SH4-202 CONFIG_CPU_SUBTYPE_SH4_202 \
     ST40RA/ST40STB1 CONFIG_CPU_SUBTYPE_ST40STB1 \
     ST40GX1 CONFIG_CPU_SUBTYPE_ST40GX1" SH7708
 if [ "$CONFIG_CPU_SUBTYPE_SH7300" = "y" ]; then
@@ -95,6 +98,19 @@
    define_bool CONFIG_CPU_SH3 n
    define_bool CONFIG_CPU_SH4 y
 fi
+if [ "$CONFIG_CPU_SUBTYPE_SH7751R" = "y" ]; then
+   define_bool CONFIG_CPU_SH3 n
+   define_bool CONFIG_CPU_SH4 y
+   define_bool CONFIG_CPU_SUBTYPE_SH7751 y
+fi
+if [ "$CONFIG_CPU_SUBTYPE_SH4_202" = "y" ]; then
+   define_bool CONFIG_CPU_SH3 n
+   define_bool CONFIG_CPU_SH4 y
+fi
+if [ "$CONFIG_CPU_SUBTYPE_SH7751R" = "y" -o \
+     "$CONFIG_CPU_SUBTYPE_SH4_202" = "y" ]; then
+   bool 'Use 2-way set associative caches' CONFIG_SH_CACHE_ASSOC
+fi
 if [ "$CONFIG_CPU_SUBTYPE_ST40STB1" = "y" ]; then
    define_bool CONFIG_CPU_SH3 n
    define_bool CONFIG_CPU_SH4 y
@@ -157,6 +173,9 @@
    hex 'Physical memory start address' CONFIG_MEMORY_START 08000000
    hex 'Physical memory size' CONFIG_MEMORY_SIZE 00400000
 fi
+
+hex 'Link address offset for booting' CONFIG_BOOT_LINK_OFFSET 00210000
+
 dep_bool 'Enable OC RAM zone (experimental)' CONFIG_SCRATCH_SPACE $CONFIG_EXPERIMENTAL
 
 if [ "$CONFIG_CPU_SUBTYPE_ST40" = "y" ]; then
@@ -332,9 +351,9 @@
 #
 source drivers/input/Config.in
 
-if [ "$CONFIG_SH_DREAMCAST" = "y" ]; then
-   source drivers/maple/Config.in
-fi
+#if [ "$CONFIG_SH_DREAMCAST" = "y" ]; then
+#   source drivers/maple/Config.in
+#fi
 
 mainmenu_option next_comment
 comment 'Character devices'
diff -urN linux-2.4.23-pre5/arch/sh/kernel/Makefile linux-2.4.23-pre6/arch/sh/kernel/Makefile
--- linux-2.4.23-pre5/arch/sh/kernel/Makefile	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/sh/kernel/Makefile	2003-10-01 10:38:45.000000000 -0700
@@ -93,6 +93,9 @@
 obj-$(CONFIG_SH_KEYWEST)	+= mach_keywest.o setup_keywest.o io_keywest.o
 machine-specific-objs		+= mach_keywest.o setup_keywest.o io_keywest.o
 
+obj-$(CONFIG_SH_SH4202_MICRODEV)+= mach_microdev.o setup_microdev.o io_microdev.o irq_microdev.o
+machine-specific-objs		+= mach_microdev.o setup_microdev.o io_microdev.o irq_microdev.o
+
 # Doesn't compile well, so don't include in machine-specific-objs
 obj-$(CONFIG_HD64465)		+= setup_hd64465.o io_hd64465.o hd64465_gpio.o
 obj-$(CONFIG_SH_DMIDA)		+= mach_dmida.o
diff -urN linux-2.4.23-pre5/arch/sh/kernel/entry.S linux-2.4.23-pre6/arch/sh/kernel/entry.S
--- linux-2.4.23-pre5/arch/sh/kernel/entry.S	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/sh/kernel/entry.S	2003-10-01 10:38:45.000000000 -0700
@@ -589,6 +589,7 @@
 	mov	#0, k1
 	mov.b	k1, @k0
 #endif
+	mov.l	@r15+, k2		! restore EXPEVT
 	mov	k4, r15
 	rte
 	 nop
@@ -678,6 +679,7 @@
 9:	mov.l	3f, k1
 	!
 	! Save the user registers on the stack.
+	mov.l	k2, @-r15	! EXPEVT
 	add	#-4, r15	! placeholder
 	!
 	sts.l	macl, @-r15
diff -urN linux-2.4.23-pre5/arch/sh/kernel/fpu.c linux-2.4.23-pre6/arch/sh/kernel/fpu.c
--- linux-2.4.23-pre5/arch/sh/kernel/fpu.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/sh/kernel/fpu.c	2003-10-01 10:38:45.000000000 -0700
@@ -18,6 +18,14 @@
 #include <asm/processor.h>
 #include <asm/io.h>
 
+/* The PR (precision) bit in the FP Status Register must be clear when
+ * an frchg instruction is executed, otherwise the instruction is undefined.
+ * Executing frchg with PR set causes a trap on some SH4 implementations.
+ */
+
+#define FPSCR_RCHG 0x00000000
+
+
 /*
  * Save FPU registers onto task structure.
  * Assume called with FPU enabled (SR.FD=0).
@@ -61,9 +69,11 @@
 		     "fmov.s	fr3, @-%0\n\t"
 		     "fmov.s	fr2, @-%0\n\t"
 		     "fmov.s	fr1, @-%0\n\t"
-		     "fmov.s	fr0, @-%0"
+		     "fmov.s	fr0, @-%0\n\t"
+		     "lds	%2, fpscr\n\t"
 		     : /* no output */
 		     : "r" ((char *)(&tsk->thread.fpu.hard.status)),
+		       "r" (FPSCR_RCHG),
 		       "r" (FPSCR_INIT)
 		     : "memory");
 
@@ -112,7 +122,7 @@
 		     "lds.l	@%0+, fpscr\n\t"
 		     "lds.l	@%0+, fpul\n\t"
 		     : /* no output */
-		     : "r" (&tsk->thread.fpu), "r" (FPSCR_INIT)
+		     : "r" (&tsk->thread.fpu), "r" (FPSCR_RCHG)
 		     : "memory");
 }
 
@@ -160,9 +170,10 @@
 		     "fsts	fpul, fr13\n\t"
 		     "fsts	fpul, fr14\n\t"
 		     "fsts	fpul, fr15\n\t"
-		     "frchg"
+		     "frchg\n\t"
+		     "lds	%2, fpscr\n\t"
 		     : /* no output */
-		     : "r" (0), "r" (FPSCR_INIT));
+		     : "r" (0), "r" (FPSCR_RCHG), "r" (FPSCR_INIT));
 }
 
 /**
diff -urN linux-2.4.23-pre5/arch/sh/kernel/io_microdev.c linux-2.4.23-pre6/arch/sh/kernel/io_microdev.c
--- linux-2.4.23-pre5/arch/sh/kernel/io_microdev.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/arch/sh/kernel/io_microdev.c	2003-10-01 10:38:45.000000000 -0700
@@ -0,0 +1,219 @@
+/* 
+ * linux/arch/sh/kernel/io_microdev.c
+ *
+ * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com)
+ *
+ * SuperH SH4-202 MicroDev board support.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <asm/io.h>
+
+
+	/*
+	 *	we need to have a 'safe' address to re-direct all I/O requests
+	 *	that we do not explicitly wish to handle. This safe address
+	 *	must have the following properies:
+	 *
+	 *		* writes are ignored (no exception)
+	 *		* reads are benign (no side-effects)
+	 *		* accesses of width 1, 2 and 4-bytes are all valid.
+	 *
+	 *	The Processor Version Register (PVR) has these properties.
+	 */
+#define	PVR	0xff000030	/* Processor Version Register */
+
+
+#define	IO_LAN91C111	0x300ul	/* I/O port for SMSC  LAN91C111 Ethernet chip */
+
+#define PORT2ADDR(x) (microdev_isa_port2addr(x))
+
+
+static inline void delay(void)
+{
+	ctrl_inw(0xa0000000);
+}
+
+unsigned char microdev_inb(unsigned long port)
+{
+	return *(volatile unsigned char*)PORT2ADDR(port);
+}
+
+unsigned short microdev_inw(unsigned long port)
+{
+	return *(volatile unsigned short*)PORT2ADDR(port);
+}
+
+unsigned int microdev_inl(unsigned long port)
+{
+	return *(volatile unsigned int*)PORT2ADDR(port);
+}
+
+void microdev_outb(unsigned char b, unsigned long port)
+{
+		/*
+		 *	There is a board feature with the current SH4-202 MicroDev 
+		 *	in that the 2 byte enables (nBE0 and nBE1) are tied together (and to the 
+		 *	Chip Select Line (Ethernet_CS)). Due to this conectivity, it is not possible
+		 *	to safely perform 8-bit writes to the Ethernet registers, as 16-bits
+		 *	will be consumed from the Data lines (corrupting the other byte).
+		 *	Hence, this function is written to impliment 16-bit read/modify/write
+		 *	for all byte-wide acceses.
+		 *
+		 *	Note: there is no problem with byte READS (even or odd).
+		 *
+		 *			Sean McGoogan - 16th June 2003.
+		 */
+	if ( (port>=IO_LAN91C111) && (port<IO_LAN91C111+0x10ul) )
+	{
+			/*
+			 * Then are trying to perform a byte-write to the LAN91C111.
+			 * This needs special care.
+			 */
+		if (port%2==1)					/* is the port odd ? */
+		{
+			const unsigned long evenPort = port-1;	/* unset bit-0, i.e. make even */
+			unsigned short word;			/* temp variable */
+				/*
+				 * do a 16-bit read/write to write to 'port', preserving even byte.
+				 *	Even addresses are bits 0-7
+				 *	Odd  addresses are bits 8-15
+				 */
+			word = microdev_inw(evenPort);
+			word = (word & 0xffu) | (b << 8);
+			microdev_outw(word, evenPort);
+		}
+		else	/* else, we are trying to do an even byte write */
+		{
+			unsigned short word;			/* temp variable */
+				/*
+				 * do a 16-bit read/write to write to 'port', preserving odd byte.
+				 *	Even addresses are bits 0-7
+				 *	Odd  addresses are bits 8-15
+				 */
+			word = microdev_inw(port);
+			word = (word & 0xff00u) | (b);
+			microdev_outw(word, port);
+		}
+	}
+	else
+	{
+		*(volatile unsigned char*)PORT2ADDR(port) = b;
+	}
+}
+
+void microdev_outw(unsigned short b, unsigned long port)
+{
+	*(volatile unsigned short*)PORT2ADDR(port) = b;
+}
+
+void microdev_outl(unsigned int b, unsigned long port)
+{
+        *(volatile unsigned int*)PORT2ADDR(port) = b;
+}
+
+unsigned char microdev_inb_p(unsigned long port)
+{
+	unsigned char v = microdev_inb(port);
+	delay();
+	return v;
+}
+
+unsigned short microdev_inw_p(unsigned long port)
+{
+	unsigned short v = microdev_inw(port);
+	delay();
+	return v;
+}
+
+unsigned int microdev_inl_p(unsigned long port)
+{
+	unsigned int v = microdev_inl(port);
+	delay();
+	return v;
+}
+
+void microdev_outb_p(unsigned char b, unsigned long port)
+{
+	microdev_outb(b, port);
+	delay();
+}
+
+void microdev_outw_p(unsigned short b, unsigned long port)
+{
+	microdev_outw(b, port);
+	delay();
+}
+
+void microdev_outl_p(unsigned int b, unsigned long port)
+{
+	microdev_outl(b, port);
+	delay();
+}
+
+void microdev_insb(unsigned long port, void *buffer, unsigned long count)
+{
+	unsigned char *buf=buffer;
+	while(count--) *buf++=microdev_inb(port);
+}
+
+void microdev_insw(unsigned long port, void *buffer, unsigned long count)
+{
+	unsigned short *buf=buffer;
+	while(count--) *buf++=microdev_inw(port);
+}
+
+void microdev_insl(unsigned long port, void *buffer, unsigned long count)
+{
+	unsigned int *buf=buffer;
+	while(count--) *buf++=microdev_inl(port);
+}
+
+void microdev_outsb(unsigned long port, const void *buffer, unsigned long count)
+{
+	const unsigned char *buf=buffer;
+	while(count--) microdev_outb(*buf++, port);
+}
+
+void microdev_outsw(unsigned long port, const void *buffer, unsigned long count)
+{
+	const unsigned short *buf=buffer;
+	while(count--) microdev_outw(*buf++, port);
+}
+
+void microdev_outsl(unsigned long port, const void *buffer, unsigned long count)
+{
+	const unsigned int *buf=buffer;
+	while(count--) microdev_outl(*buf++, port);
+}
+
+
+/*
+ * map I/O ports to memory-mapped addresses 
+ */
+unsigned long microdev_isa_port2addr(unsigned long offset)
+{
+	unsigned long result;
+
+	if ( (offset>=IO_LAN91C111) && (offset<IO_LAN91C111+0x10ul) )
+	{
+			/*
+			 *	SMSC LAN91C111 Ethernet chip
+			 */
+		result = 0xa7500000ul + offset - IO_LAN91C111;
+	}
+	else /* if (offset <= 0xfffful) */
+	{
+			/*
+			 *	safe default.
+			 */
+		result = PVR;
+	}
+
+	return result;
+}
+
diff -urN linux-2.4.23-pre5/arch/sh/kernel/io_shmse.c linux-2.4.23-pre6/arch/sh/kernel/io_shmse.c
--- linux-2.4.23-pre5/arch/sh/kernel/io_shmse.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/sh/kernel/io_shmse.c	2003-10-01 10:38:45.000000000 -0700
@@ -1,17 +1,67 @@
 /*
  * linux/arch/sh/kernel/io_shmse.c
  *
- * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp>
- * Based largely on io_se.c
- *
- * I/O routine for SH-Mobile SolutionEngine.
+ * Copyright (C) 2003 YOSHII Takashi <yoshii-takashi@hitachi-ul.co.jp>
  */
 
 #include <linux/kernel.h>
-#include <linux/types.h>
-#include <asm/io.h>
 #include <asm/hitachi_shmse.h>
-#include <asm/addrspace.h>
+#include <asm/io.h>
+
+#define badio(fn, a) panic("bad i/o operation %s for %08lx.", #fn, a)
+
+struct iop {
+	unsigned long start, end;
+	unsigned long base;
+	struct iop *(*check)(struct iop *p, unsigned long port);
+	unsigned char (*inb)(struct iop *p, unsigned long port);
+	unsigned short (*inw)(struct iop *p, unsigned long port);
+	void (*outb)(struct iop *p, unsigned char value, unsigned long port);
+	void (*outw)(struct iop *p, unsigned short value, unsigned long port);
+};
+ 
+struct iop *simple_check(struct iop *p, unsigned long port){
+	if((p->start <= port) && (port <= p->end))
+		return p;
+	else
+		badio(check, port);
+}
+
+unsigned short simple_inw(struct iop *p, unsigned long port){
+	return *(unsigned short*)(p->base+port);
+}
+
+void simple_outw(struct iop *p, unsigned short value, unsigned long port){
+	*(unsigned short*)(p->base+port)=value;
+}
+
+unsigned char bad_inb(struct iop *p, unsigned long port)
+{
+	badio(inb, port);
+}
+
+void bad_outb(struct iop *p, unsigned char value, unsigned long port){
+	badio(inw, port);
+}
+
+/* MSTLANEX01 LAN at 0xb400:0000 */
+static struct iop laniop = {
+	.start = 0x200,	// device is at 0x300, but start here for probing.
+	.end = 0x30f,
+	.base = 0xb4000000,
+	.check = simple_check,
+	.inb = bad_inb,
+	.inw = simple_inw,
+	.outb = bad_outb,
+	.outw = simple_outw,
+};
+
+static __inline__ struct iop *port2iop(unsigned long port){
+	if(laniop.check(&laniop, port))
+		return &laniop;
+	else
+		badio(check, port); /* XXX: dummy fallback routine? */
+}
 
 static inline void delay(void)
 {
@@ -19,151 +69,117 @@
 	ctrl_inw(0xac000000);
 }
 
-#define maybebadio(name,port) \
-  printk("bad PC-like io %s for port 0x%lx at 0x%08lx\n", \
-	#name, (port), (volatile unsigned long) __builtin_return_address(0))
-
 unsigned char shmse_inb(unsigned long port)
 {
-	if (PXSEG(port)){
-		return *(volatile unsigned char *) port; 
-	}else{
-		maybebadio(inb, port);
-		return 0;
-	}
+	struct iop *p=port2iop(port);
+	return (p->inb)(p, port);
 }
 
 unsigned char shmse_inb_p(unsigned long port)
 {
-	unsigned char v;
-
-	if (PXSEG(port)){
-		v = *(volatile unsigned char *)port;
-	}else{
-		maybebadio(inb_p, port);
-		return 0;
-	}
+	unsigned char v=shmse_inb(port);
 	delay();
 	return v;
 }
 
 unsigned short shmse_inw(unsigned long port)
 {
-	if (PXSEG(port)){
-		return *(volatile unsigned short *) port; 
-	}else{
-		maybebadio(inw, port);
-		return 0;
-	}
+	struct iop *p=port2iop(port);
+	return (p->inw)(p, port);
 }
 
 unsigned int shmse_inl(unsigned long port)
 {
-	if (PXSEG(port)){
-		return *(volatile unsigned short *) port; 
-	}else{
-		maybebadio(inl, port);
-		return 0;
-	}
+	badio(inl, port);
 }
 
 void shmse_outb(unsigned char value, unsigned long port)
 {
-	if (PXSEG(port)){
-		*(volatile unsigned char *)port = value; 
-	}else{
-		maybebadio(outb, port);
-	}
+	struct iop *p=port2iop(port);
+	(p->outb)(p, value, port);
 }
 
 void shmse_outb_p(unsigned char value, unsigned long port)
 {
-	if (PXSEG(port)){
-		*(volatile unsigned char *)port = value; 
-	}else{
-		maybebadio(outb_p, port);
-	}
+	shmse_outb(value, port);
 	delay();
 }
 
 void shmse_outw(unsigned short value, unsigned long port)
 {
-	if (PXSEG(port)){
-		*(volatile unsigned short *)port = value; 
-	}else{
-		maybebadio(outw, port);
-	}
+	struct iop *p=port2iop(port);
+	(p->outw)(p, value, port);
 }
 
 void shmse_outl(unsigned int value, unsigned long port)
 {
-	if (PXSEG(port)){
-		*(volatile unsigned long *)port = value; 
-	}else{
-		maybebadio(outl, port);
-	}
+	badio(outl, port);
 }
 
 void shmse_insb(unsigned long port, void *addr, unsigned long count)
 {
-	unsigned char *p = addr;
-	while (count--) *p++ = shmse_inb(port);
+	unsigned char *a = addr;
+	struct iop *p=port2iop(port);
+	while (count--) *a++ = (p->inb)(p, port);
 }
 
 void shmse_insw(unsigned long port, void *addr, unsigned long count)
 {
-	unsigned short *p = addr;
-	while (count--) *p++ = shmse_inw(port);
+	unsigned short *a = addr;
+	struct iop *p=port2iop(port);
+	while (count--) *a++ = (p->inw)(p, port);
 }
 
 void shmse_insl(unsigned long port, void *addr, unsigned long count)
 {
-	maybebadio(insl, port);
+	badio(insl, port);
 }
 
 void shmse_outsb(unsigned long port, const void *addr, unsigned long count)
 {
-	unsigned char *p = (unsigned char *)addr;
-	while (count--) shmse_outb(*p++, port);
+	unsigned char *a = (unsigned char*)addr;
+	struct iop *p=port2iop(port);
+	while (count--) (p->outb)(p, *a++, port);
 }
 
 void shmse_outsw(unsigned long port, const void *addr, unsigned long count)
 {
-	unsigned short *p = (unsigned short *)addr;
-	while (count--) shmse_outw(*p++, port);
+	unsigned short *a = (unsigned short*)addr;
+	struct iop *p=port2iop(port);
+	while (count--) (p->outw)(p, *a++, port);
 }
 
 void shmse_outsl(unsigned long port, const void *addr, unsigned long count)
 {
-	maybebadio(outsw, port);
+	badio(outsw, port);
 }
 
 unsigned char shmse_readb(unsigned long addr)
 {
-	return *(volatile unsigned char*)addr;
+	badio(readb, addr);
 }
 
 unsigned short shmse_readw(unsigned long addr)
 {
-	return *(volatile unsigned short*)addr;
+	badio(readw, addr);
 }
 
 unsigned int shmse_readl(unsigned long addr)
 {
-	return *(volatile unsigned long*)addr;
+	badio(readl, addr);
 }
 
 void shmse_writeb(unsigned char b, unsigned long addr)
 {
-	*(volatile unsigned char*)addr = b;
+	badio(writeb, addr);
 }
 
 void shmse_writew(unsigned short b, unsigned long addr)
 {
-	*(volatile unsigned short*)addr = b;
+	badio(writew, addr);
 }
 
 void shmse_writel(unsigned int b, unsigned long addr)
 {
-        *(volatile unsigned long*)addr = b;
+	badio(writel, addr);
 }
diff -urN linux-2.4.23-pre5/arch/sh/kernel/irq_microdev.c linux-2.4.23-pre6/arch/sh/kernel/irq_microdev.c
--- linux-2.4.23-pre5/arch/sh/kernel/irq_microdev.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/arch/sh/kernel/irq_microdev.c	2003-10-01 10:38:45.000000000 -0700
@@ -0,0 +1,158 @@
+/* 
+ * linux/arch/sh/kernel/irq_microdev.c
+ *
+ * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com)
+ *
+ * SuperH SH4-202 MicroDev board support.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq_microdev.h>
+
+#define NUM_EXTERNAL_IRQS 16	/* IRL0 .. IRL15 */
+
+
+static const struct 
+{
+	unsigned char fpgaIrq;
+	unsigned char mapped;
+	const char *name;
+}	fpgaIrqTable[NUM_EXTERNAL_IRQS] = 
+{
+	{ 0,	0,	"unused" },		/* IRQ #0	IRL=15	0x200  */
+	{ 0,	0,	"unused" },		/* IRQ #1	IRL=14	0x220  */
+	{ 0,	0,	"unused" },		/* IRQ #2	IRL=13	0x240  */
+	{ 18,	1,	"Ethernet" },		/* IRQ #3	IRL=12	0x260  */
+	{ 0,	0,	"unused" },		/* IRQ #4	IRL=11	0x280  */
+	{ 0,	0,	"unused" },		/* IRQ #5	IRL=10	0x2a0  */
+	{ 0,	0,	"unused" },		/* IRQ #6	IRL=9	0x2c0  */
+	{ 0,	0,	"unused" },		/* IRQ #7	IRL=8	0x2e0  */
+	{ 8,	1,	"PCI INTA" },		/* IRQ #8	IRL=7	0x300  */
+	{ 9,	1,	"PCI INTB" },		/* IRQ #9	IRL=6	0x320  */
+	{ 10,	1,	"PCI INTC" },		/* IRQ #10	IRL=5	0x340  */
+	{ 11,	1,	"PCI INTD" },		/* IRQ #11	IRL=4	0x360  */
+	{ 0,	0,	"unused" },		/* IRQ #12	IRL=3	0x380  */
+	{ 0,	0,	"unused" },		/* IRQ #13	IRL=2	0x3a0  */
+	{ 0,	0,	"unused" },		/* IRQ #14	IRL=1	0x3c0  */
+	{ 0,	0,	"unused" },		/* IRQ #15	IRL=0	0x3e0  */
+};
+
+static void enable_microdev_irq(unsigned int irq);
+static void disable_microdev_irq(unsigned int irq);
+
+	/* shutdown is same as "disable" */
+#define shutdown_microdev_irq disable_microdev_irq
+
+static void mask_and_ack_microdev(unsigned int);
+static void end_microdev_irq(unsigned int irq);
+
+static unsigned int startup_microdev_irq(unsigned int irq)
+{
+	enable_microdev_irq(irq);
+	return 0;		/* never anything pending */
+}
+
+static struct hw_interrupt_type microdev_irq_type = {
+	"MicroDev-IRQ",
+	startup_microdev_irq,
+	shutdown_microdev_irq,
+	enable_microdev_irq,
+	disable_microdev_irq,
+	mask_and_ack_microdev,
+	end_microdev_irq
+};
+
+static void disable_microdev_irq(unsigned int irq)
+{
+	unsigned int flags; 
+	unsigned int fpgaIrq; 
+
+	if (irq >= NUM_EXTERNAL_IRQS) return;
+	if (!fpgaIrqTable[irq].mapped) return;
+
+	fpgaIrq = fpgaIrqTable[irq].fpgaIrq;
+
+		/* disable interrupts */
+	save_and_cli(flags);
+
+		/* disable interupts on the FPGA INTC register */
+	ctrl_outl(MICRODEV_FPGA_INTC_MASK(fpgaIrq), MICRODEV_FPGA_INTDSB_REG);
+
+		/* restore interrupts */
+	restore_flags(flags);
+}
+
+static void enable_microdev_irq(unsigned int irq)
+{
+	unsigned long priorityReg, priorities, pri;
+	unsigned int flags; 
+	unsigned int fpgaIrq; 
+
+
+	if (irq >= NUM_EXTERNAL_IRQS) return;
+	if (!fpgaIrqTable[irq].mapped) return;
+
+	pri = 15 - irq;
+
+	fpgaIrq = fpgaIrqTable[irq].fpgaIrq;
+	priorityReg = MICRODEV_FPGA_INTPRI_REG(fpgaIrq);
+
+		/* disable interrupts */
+	save_and_cli(flags);
+
+		/* set priority for the interrupt */
+	priorities = ctrl_inl(priorityReg);
+	priorities &= ~MICRODEV_FPGA_INTPRI_MASK(fpgaIrq);
+	priorities |= MICRODEV_FPGA_INTPRI_LEVEL(fpgaIrq, pri);
+	ctrl_outl(priorities, priorityReg);
+	
+		/* enable interupts on the FPGA INTC register */
+	ctrl_outl(MICRODEV_FPGA_INTC_MASK(fpgaIrq), MICRODEV_FPGA_INTENB_REG);
+
+		/* restore interrupts */
+	restore_flags(flags);
+}
+
+	/* This functions sets the desired irq handler to be a MicroDev type */
+static void __init make_microdev_irq(unsigned int irq)
+{
+	disable_irq_nosync(irq);
+	irq_desc[irq].handler = &microdev_irq_type;
+	disable_microdev_irq(irq);
+}
+
+static void mask_and_ack_microdev(unsigned int irq)
+{
+	disable_microdev_irq(irq);
+}
+
+static void end_microdev_irq(unsigned int irq)
+{
+	if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+	{
+		enable_microdev_irq(irq);
+	}
+}
+
+extern void __init init_microdev_irq(void)
+{
+	int i;
+
+		/* disable interupts on the FPGA INTC register */
+	ctrl_outl(~0ul, MICRODEV_FPGA_INTDSB_REG);
+
+	for (i = 0; i < NUM_EXTERNAL_IRQS; i++)
+	{
+		make_microdev_irq(i);
+	}
+}
+
+
diff -urN linux-2.4.23-pre5/arch/sh/kernel/mach_microdev.c linux-2.4.23-pre6/arch/sh/kernel/mach_microdev.c
--- linux-2.4.23-pre5/arch/sh/kernel/mach_microdev.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/arch/sh/kernel/mach_microdev.c	2003-10-01 10:38:45.000000000 -0700
@@ -0,0 +1,77 @@
+/*
+ * linux/arch/sh/kernel/mach_microdev.c
+ *
+ * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com)
+ *
+ * Machine vector for the SuperH SH4-202 MicroDev board.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+
+#include <asm/machvec.h>
+#include <asm/rtc.h>
+#include <asm/machvec_init.h>
+
+#include <asm/io_microdev.h>
+#include <asm/irq_microdev.h>
+
+void setup_microdev(void);
+
+/*
+ * The Machine Vector
+ */
+
+struct sh_machine_vector mv_sh4202_microdev __initmv = {
+	mv_name:		"SH4-202 MicroDev",
+
+	mv_nr_irqs:		72,		/* QQQ need to check this - use the MACRO */ 
+
+	mv_inb:			microdev_inb,
+	mv_inw:			microdev_inw,
+	mv_inl:			microdev_inl,
+	mv_outb:		microdev_outb,
+	mv_outw:		microdev_outw,
+	mv_outl:		microdev_outl,
+
+	mv_inb_p:		microdev_inb_p,
+	mv_inw_p:		microdev_inw_p,
+	mv_inl_p:		microdev_inl_p,
+	mv_outb_p:		microdev_outb_p,
+	mv_outw_p:		microdev_outw_p,
+	mv_outl_p:		microdev_outl_p,
+
+	mv_insb:		microdev_insb,
+	mv_insw:		microdev_insw,
+	mv_insl:		microdev_insl,
+	mv_outsb:		microdev_outsb,
+	mv_outsw:		microdev_outsw,
+	mv_outsl:		microdev_outsl,
+
+	mv_readb:		generic_readb,
+	mv_readw:		generic_readw,
+	mv_readl:		generic_readl,
+	mv_writeb:		generic_writeb,
+	mv_writew:		generic_writew,
+	mv_writel:		generic_writel,
+
+	mv_ioremap:		generic_ioremap,
+	mv_iounmap:		generic_iounmap,
+
+	mv_isa_port2addr:	microdev_isa_port2addr,
+
+	mv_init_arch:		setup_microdev,
+
+	mv_init_irq:		init_microdev_irq,
+
+	mv_rtc_gettimeofday:	sh_rtc_gettimeofday,
+	mv_rtc_settimeofday:	sh_rtc_settimeofday,
+
+	mv_hw_sh4202_microdev:	1,
+};
+ALIAS_MV(sh4202_microdev)
+
diff -urN linux-2.4.23-pre5/arch/sh/kernel/setup_microdev.c linux-2.4.23-pre6/arch/sh/kernel/setup_microdev.c
--- linux-2.4.23-pre5/arch/sh/kernel/setup_microdev.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/arch/sh/kernel/setup_microdev.c	2003-10-01 10:38:45.000000000 -0700
@@ -0,0 +1,37 @@
+/* 
+ * linux/arch/sh/kernel/setup_microdev.c
+ *
+ * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com)
+ *
+ * SuperH SH4-202 MicroDev board support.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <asm/io.h>
+
+	/* General-Purpose base address on CPU-board FPGA */
+#define	MICRODEV_FPGA_GP_BASE		0xa6100000ul
+
+	/* Address of Cache Control Register */
+#define CCR				0xff00001cul
+
+/*
+ * Initialize the board
+ */
+void __init setup_microdev(void)
+{
+	int * const fpgaRevisionRegister = (int*)(MICRODEV_FPGA_GP_BASE + 0x8ul);
+	const int fpgaRevision = *fpgaRevisionRegister;
+	int * const CacheControlRegister = (int*)CCR;
+
+	printk("SuperH SH4-202 MicroDev board (FPGA rev: 0x%0x, CCR: 0x%0x)\n",
+		fpgaRevision, *CacheControlRegister);
+
+	return;
+}
+
+
diff -urN linux-2.4.23-pre5/arch/sh/kernel/setup_shmse.c linux-2.4.23-pre6/arch/sh/kernel/setup_shmse.c
--- linux-2.4.23-pre5/arch/sh/kernel/setup_shmse.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/sh/kernel/setup_shmse.c	2003-10-01 10:38:45.000000000 -0700
@@ -15,6 +15,12 @@
 
 void __init init_shmse_IRQ(void)
 {
+	ctrl_outw(0x0028, 0xb0a00000);	// mode set [active low].
+	ctrl_outw(0x000a, INTC_ICR1);	// IRQ mode; IRQ0,1 enable.
+ 	/* PC_IRQ[0-3] -> IRQ0 (32) */
+        make_ipr_irq( IRQ0_IRQ, IRQ0_IPR_ADDR, IRQ0_IPR_POS, 0x0f-IRQ0_IRQ);
+        /* A_IRQ[0-3] -> IRQ1 (33) */
+        make_ipr_irq( IRQ1_IRQ, IRQ1_IPR_ADDR, IRQ1_IPR_POS, 0x0f-IRQ1_IRQ);
 }
 
 void __init setup_shmse(void)
diff -urN linux-2.4.23-pre5/arch/sh/kernel/sh_ksyms.c linux-2.4.23-pre6/arch/sh/kernel/sh_ksyms.c
--- linux-2.4.23-pre5/arch/sh/kernel/sh_ksyms.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/sh/kernel/sh_ksyms.c	2003-10-01 10:38:45.000000000 -0700
@@ -19,6 +19,7 @@
 #include <asm/hardirq.h>
 #include <asm/delay.h>
 #include <asm/pgalloc.h>
+#include <asm/div64.h>
 #include <linux/irq.h>
 
 extern void dump_thread(struct pt_regs *, struct user *);
@@ -38,6 +39,7 @@
 
 /* Networking helper routines. */
 EXPORT_SYMBOL(csum_partial_copy);
+EXPORT_SYMBOL(csum_partial_copy_generic);
 
 EXPORT_SYMBOL(strtok);
 EXPORT_SYMBOL(strpbrk);
@@ -45,6 +47,7 @@
 EXPORT_SYMBOL(strlen);
 EXPORT_SYMBOL(strnlen);
 EXPORT_SYMBOL(strchr);
+EXPORT_SYMBOL(strrchr);
 EXPORT_SYMBOL(strcat);
 EXPORT_SYMBOL(strncat);
 
@@ -81,6 +84,8 @@
 EXPORT_SYMBOL(__down_interruptible);
 EXPORT_SYMBOL(__down_trylock);
 
+/* Delay loops */
+EXPORT_SYMBOL(__ndelay);
 EXPORT_SYMBOL(__udelay);
 EXPORT_SYMBOL(__const_udelay);
 
@@ -105,3 +110,4 @@
 EXPORT_SYMBOL(flush_dcache_page);
 #endif
 EXPORT_SYMBOL(flush_tlb_page);
+EXPORT_SYMBOL(__xdiv64_32);
diff -urN linux-2.4.23-pre5/arch/sh/lib/Makefile linux-2.4.23-pre6/arch/sh/lib/Makefile
--- linux-2.4.23-pre5/arch/sh/lib/Makefile	2001-09-08 12:29:09.000000000 -0700
+++ linux-2.4.23-pre6/arch/sh/lib/Makefile	2003-10-01 10:38:45.000000000 -0700
@@ -4,7 +4,7 @@
 
 L_TARGET = lib.a
 obj-y  = delay.o memcpy.o memset.o memmove.o memchr.o old-checksum.o \
-	 checksum.o strcasecmp.o strlen.o
+	 checksum.o strcasecmp.o strlen.o udivdi3.o div64.o
 
 USE_STANDARD_AS_RULE := true
 
diff -urN linux-2.4.23-pre5/arch/sh/lib/div64.S linux-2.4.23-pre6/arch/sh/lib/div64.S
--- linux-2.4.23-pre5/arch/sh/lib/div64.S	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/arch/sh/lib/div64.S	2003-10-01 10:38:45.000000000 -0700
@@ -0,0 +1,46 @@
+/*	
+ * unsigned long long __xdiv64_32(unsigned long long n, unsigned long d); 
+ */
+
+#include <linux/linkage.h>
+
+.text
+ENTRY(__xdiv64_32)
+#ifdef  __LITTLE_ENDIAN__
+	mov	r4, r0
+	mov	r5, r1
+#else
+	mov	r4, r1
+	mov	r5, r0
+#endif
+	cmp/hs	r6, r1
+	bf.s	1f
+	mov	#0, r2
+
+	mov	r1, r2
+	mov	#0, r3
+	div0u
+	.rept	32
+	rotcl	r2
+	div1	r6, r3
+	.endr
+	rotcl	r2
+	mul.l	r6, r2
+	sts	macl, r3
+	sub	r3, r1
+1:
+	div0u
+	.rept	32
+	rotcl	r0
+	div1	r6, r1
+	.endr
+#ifdef  __LITTLE_ENDIAN__
+	mov	r2, r1
+	rts
+	rotcl	r0
+#else
+	rotcl	r0
+	mov	r0, r1
+	rts
+	mov	r2, r0
+#endif
diff -urN linux-2.4.23-pre5/arch/sh/lib/udivdi3.c linux-2.4.23-pre6/arch/sh/lib/udivdi3.c
--- linux-2.4.23-pre5/arch/sh/lib/udivdi3.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/arch/sh/lib/udivdi3.c	2003-10-01 10:38:45.000000000 -0700
@@ -0,0 +1,16 @@
+/*
+ * Simple __udivdi3 function which doesn't use FPU.
+ */
+
+#include <linux/types.h>
+
+extern u64 __xdiv64_32(u64 n, u32 d);
+extern void panic(const char * fmt, ...);
+
+u64 __udivdi3(u64 n, u64 d)
+{
+	if (d & ~0xffffffff)
+		panic("Need true 64-bit/64-bit division");
+	return __xdiv64_32(n, (u32)d);
+}
+
diff -urN linux-2.4.23-pre5/arch/sh/mm/__clear_user_page-sh4.S linux-2.4.23-pre6/arch/sh/mm/__clear_user_page-sh4.S
--- linux-2.4.23-pre5/arch/sh/mm/__clear_user_page-sh4.S	2001-09-08 12:29:09.000000000 -0700
+++ linux-2.4.23-pre6/arch/sh/mm/__clear_user_page-sh4.S	1969-12-31 16:00:00.000000000 -0800
@@ -1,49 +0,0 @@
-/* $Id$
- *
- * __clear_user_page implementation of SuperH
- *
- * Copyright (C) 2001  Niibe Yutaka & Kaz Kojima
- *
- */
-
-/*
- * __clear_user_page
- * @to: P1 address (with same color)
- * @orig_to: P1 address
- *
- * void __clear_user_page(void *to, void *orig_to)
- */
-
-/*
- * r0 --- scratch 
- * r4 --- to
- * r5 --- orig_to
- * r6 --- to + 4096
- */
-#include <linux/linkage.h>
-ENTRY(__clear_user_page)
-	mov	r4,r6
-	mov.w	.L4096,r0
-	add	r0,r6
-	mov	#0,r0
-	!
-1:	ocbi	@r5
-	add	#32,r5
-	movca.l	r0,@r4
-	mov	r4,r1
-	add	#32,r4
-	mov.l	r0,@-r4
-	mov.l	r0,@-r4
-	mov.l	r0,@-r4
-	mov.l	r0,@-r4
-	mov.l	r0,@-r4
-	mov.l	r0,@-r4
-	mov.l	r0,@-r4
-	add	#28,r4
-	cmp/eq	r6,r4
-	bf/s	1b
-	 ocbwb	@r1
-	!
-	rts
-	 nop
-.L4096:	.word	4096
diff -urN linux-2.4.23-pre5/arch/sh/mm/__copy_user_page-sh4.S linux-2.4.23-pre6/arch/sh/mm/__copy_user_page-sh4.S
--- linux-2.4.23-pre5/arch/sh/mm/__copy_user_page-sh4.S	2001-09-08 12:29:09.000000000 -0700
+++ linux-2.4.23-pre6/arch/sh/mm/__copy_user_page-sh4.S	1969-12-31 16:00:00.000000000 -0800
@@ -1,69 +0,0 @@
-/* $Id: __copy_user_page-sh4.S,v 1.1 2001/07/23 09:02:17 gniibe Exp $
- *
- * __copy_user_page implementation of SuperH
- *
- * Copyright (C) 2001  Niibe Yutaka & Kaz Kojima
- *
- */
-
-/*
- * __copy_user_page
- * @to: P1 address (with same color)
- * @from: P1 address
- * @orig_to: P1 address
- *
- * void __copy_user_page(void *to, void *from, void *orig_to)
- */
-
-/*
- * r0, r1, r2, r3, r4, r5, r6, r7 --- scratch 
- * r8 --- from + 4096
- * r9 --- orig_to
- * r10 --- to
- * r11 --- from
- */
-#include <linux/linkage.h>
-ENTRY(__copy_user_page)
-	mov.l	r8,@-r15
-	mov.l	r9,@-r15
-	mov.l	r10,@-r15
-	mov.l	r11,@-r15
-	mov	r4,r10
-	mov	r5,r11
-	mov	r6,r9
-	mov	r5,r8
-	mov.w	.L4096,r0
-	add	r0,r8
-	!
-1:	ocbi	@r9
-	add	#32,r9
-	mov.l	@r11+,r0
-	mov.l	@r11+,r1
-	mov.l	@r11+,r2
-	mov.l	@r11+,r3
-	mov.l	@r11+,r4
-	mov.l	@r11+,r5
-	mov.l	@r11+,r6
-	mov.l	@r11+,r7
-	movca.l	r0,@r10
-	mov	r10,r0
-	add	#32,r10
-	mov.l	r7,@-r10
-	mov.l	r6,@-r10
-	mov.l	r5,@-r10
-	mov.l	r4,@-r10
-	mov.l	r3,@-r10
-	mov.l	r2,@-r10
-	mov.l	r1,@-r10
-	ocbwb	@r0
-	cmp/eq	r11,r8
-	bf/s	1b
-	 add	#28,r10
-	!
-	mov.l	@r15+,r11
-	mov.l	@r15+,r10
-	mov.l	@r15+,r9
-	mov.l	@r15+,r8
-	rts
-	 nop
-.L4096:	.word	4096
diff -urN linux-2.4.23-pre5/arch/sh/mm/cache-sh4.c linux-2.4.23-pre6/arch/sh/mm/cache-sh4.c
--- linux-2.4.23-pre5/arch/sh/mm/cache-sh4.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/sh/mm/cache-sh4.c	2003-10-01 10:38:45.000000000 -0700
@@ -32,8 +32,17 @@
 #define CCR_CACHE_ICI	0x0800	/* IC Invalidate */
 #define CCR_CACHE_IIX	0x8000	/* IC Index Enable */
 
-/* Default CCR setup: 8k+16k-byte cache,P1-wb,enable */
-#define CCR_CACHE_VAL	(CCR_CACHE_ICE|CCR_CACHE_CB|CCR_CACHE_OCE)
+
+
+#if defined(CONFIG_SH_CACHE_ASSOC)
+#define CCR_CACHE_EMODE 0x80000000
+/* CCR setup for associative mode: 16k+32k 2-way, P1 copy-back, enable */
+#define CCR_CACHE_VAL	(CCR_CACHE_EMODE|CCR_CACHE_ENABLE|CCR_CACHE_CB)
+#else
+/* Default CCR setup: 8k+16k-byte cache, P1-copy-back, enable */
+#define CCR_CACHE_VAL	(CCR_CACHE_ENABLE|CCR_CACHE_CB)
+#endif
+
 #define CCR_CACHE_INIT	(CCR_CACHE_VAL|CCR_CACHE_OCI|CCR_CACHE_ICI)
 #define CCR_CACHE_ENABLE (CCR_CACHE_OCE|CCR_CACHE_ICE)
 
@@ -43,7 +52,7 @@
 #define CACHE_UPDATED	  2
 #define CACHE_ASSOC	  8
 
-#define CACHE_OC_WAY_SHIFT       13
+#define CACHE_OC_WAY_SHIFT       14
 #define CACHE_IC_WAY_SHIFT       13
 #define CACHE_OC_ENTRY_SHIFT      5
 #define CACHE_IC_ENTRY_SHIFT      5
@@ -53,6 +62,8 @@
 #define CACHE_IC_NUM_ENTRIES	256
 #define CACHE_OC_NUM_ENTRIES	512
 
+#define CACHE_NUM_WAYS 2
+
 static void __init
 detect_cpu_and_cache_system(void)
 {
@@ -60,6 +71,8 @@
 	cpu_data->type = CPU_ST40;
 #elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751)
 	cpu_data->type = CPU_SH7750;
+#elif defined(CONFIG_CPU_SUBTYPE_SH4_202)
+	cpu_data->type = CPU_SH4202;
 #else
 #error Unknown SH4 CPU type
 #endif
@@ -80,6 +93,26 @@
 		 */
 		unsigned long addr, data;
 
+#if defined(CONFIG_SH_CACHE_ASSOC)
+                unsigned long way;
+
+                for (way = 0; way <= CACHE_NUM_WAYS; ++way) {
+                        unsigned long waybit = way << CACHE_OC_WAY_SHIFT;
+
+		        for (addr = CACHE_OC_ADDRESS_ARRAY + waybit;
+		             addr < (CACHE_OC_ADDRESS_ARRAY + waybit +
+			             (CACHE_OC_NUM_ENTRIES << 
+                                      CACHE_OC_ENTRY_SHIFT));
+		             addr += (1 << CACHE_OC_ENTRY_SHIFT)) {
+
+			        data = ctrl_inl(addr);
+
+			        if ((data & (CACHE_UPDATED|CACHE_VALID))
+			            == (CACHE_UPDATED|CACHE_VALID))
+				        ctrl_outl(data & ~CACHE_UPDATED, addr);
+		        }
+                }
+#else
 		for (addr = CACHE_OC_ADDRESS_ARRAY;
 		     addr < (CACHE_OC_ADDRESS_ARRAY+
 			     (CACHE_OC_NUM_ENTRIES << CACHE_OC_ENTRY_SHIFT));
@@ -89,6 +122,7 @@
 			    == (CACHE_UPDATED|CACHE_VALID))
 				ctrl_outl(data & ~CACHE_UPDATED, addr);
 		}
+#endif
 	}
 
 	ctrl_outl(CCR_CACHE_INIT, CCR);
@@ -215,6 +249,12 @@
 	save_and_cli(flags);
 	jump_to_P2();
 	ctrl_outl(0, index);	/* Clear out Valid-bit */
+
+#if defined(CONFIG_SH_CACHE_ASSOC)
+	/* Must invalidate both ways for associative cache */
+	ctrl_outl(0, index | (1 << CACHE_IC_WAY_SHIFT));
+#endif
+
 	back_to_P1();
 	restore_flags(flags);
 }
@@ -225,7 +265,7 @@
 	unsigned long flags; 
 	extern void __flush_cache_4096(unsigned long addr, unsigned long phys, unsigned long exec_offset);
 
-#if defined(CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_ST40)
+#if defined(CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_ST40) || defined(CONFIG_CPU_SUBTYPE_SH4_202)
 	if (start >= CACHE_OC_ADDRESS_ARRAY) {
 		/*
 		 * SH7751 and ST40 have no restriction to handle cache.
@@ -481,3 +521,118 @@
 		up(&p3map_sem[(address & CACHE_ALIAS)>>12]);
 	}
 }
+
+
+/****************************************************************************/
+
+#if defined(CONFIG_SH_CACHE_ASSOC)
+/*
+ * It is no possible to use the approach implement in clear_page.S when we 
+ * are in 2-way set associative mode as it would only clear half the cache, in 
+ * general. For the moment we simply implement it as a iteration through the 
+ * cache flushing both ways, this in itself is not optimial as the delay latency 
+ * for interupts is probably longer than necessary!
+ *
+ * benedict.gaster.superh.com
+ */
+void __flush_dcache_all(void)
+{
+	unsigned long flags;
+	unsigned long addr;
+        unsigned long way;
+
+	save_and_cli(flags);
+#if !defined(CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_SH4_202)
+	jump_to_P2();
+#endif
+        /* Clear the U and V bits for each line and each way. On SH-4, this
+         * causes write-back if both U and V are set before the address write.
+         */
+	for (way = 0; way <= 1; ++way) {
+	        unsigned long waybit = way << CACHE_OC_WAY_SHIFT;
+
+	        /* Loop all the D-cache */
+                for (addr = CACHE_OC_ADDRESS_ARRAY + waybit;
+	             addr < (CACHE_OC_ADDRESS_ARRAY + waybit
+		             + (CACHE_OC_NUM_ENTRIES << CACHE_OC_ENTRY_SHIFT));
+	             addr += (1 << CACHE_OC_ENTRY_SHIFT)) {
+			ctrl_outl(0, addr);
+                }
+	}
+
+#if !defined(CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_SH4_202)
+	back_to_P1();
+#endif 
+	restore_flags(flags);
+}
+
+void flush_cache_4096_all(unsigned long start)
+{
+  unsigned long phys = PHYSADDR(start);
+
+  /* Loop all the D-cache */
+  flush_cache_4096(CACHE_OC_ADDRESS_ARRAY,          phys);
+}
+#endif
+
+
+
+
+
+
+/****************************************************************************/
+
+#if defined(CONFIG_SH_CACHE_ASSOC)
+/*
+ * It is no possible to use the approach implement in clear_page.S when we 
+ * are in 2-way set associative mode as it would only clear half the cache, in 
+ * general. For the moment we simply implement it as a iteration through the 
+ * cache flushing both ways, this in itself is not optimial as the delay latency 
+ * for interupts is probably longer than necessary!
+ *
+ * benedict.gaster.superh.com
+ */
+void __flush_dcache_all(void)
+{
+	unsigned long flags;
+	unsigned long addr;
+        unsigned long way;
+
+	save_and_cli(flags);
+#if !defined(CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_SH4_202)
+	jump_to_P2();
+#endif
+        /* Clear the U and V bits for each line and each way. On SH-4, this
+         * causes write-back if both U and V are set before the address write.
+         */
+	for (way = 0; way <= 1; ++way) {
+	        unsigned long waybit = way << CACHE_OC_WAY_SHIFT;
+
+	        /* Loop all the D-cache */
+                for (addr = CACHE_OC_ADDRESS_ARRAY + waybit;
+	             addr < (CACHE_OC_ADDRESS_ARRAY + waybit
+		             + (CACHE_OC_NUM_ENTRIES << CACHE_OC_ENTRY_SHIFT));
+	             addr += (1 << CACHE_OC_ENTRY_SHIFT)) {
+			ctrl_outl(0, addr);
+                }
+	}
+
+#if !defined(CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_SH4_202)
+	back_to_P1();
+#endif 
+	restore_flags(flags);
+}
+
+void flush_cache_4096_all(unsigned long start)
+{
+  unsigned long phys = PHYSADDR(start);
+
+  /* Loop all the D-cache */
+  flush_cache_4096(CACHE_OC_ADDRESS_ARRAY,          phys);
+}
+#endif
+
+
+
+
+
diff -urN linux-2.4.23-pre5/arch/sh/mm/clear_page.S linux-2.4.23-pre6/arch/sh/mm/clear_page.S
--- linux-2.4.23-pre5/arch/sh/mm/clear_page.S	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/sh/mm/clear_page.S	2003-10-01 10:38:45.000000000 -0700
@@ -194,6 +194,40 @@
 	 nop
 .L4096:	.word	4096
 
+/*
+ * __flush_cache_4096
+ *
+ * Flush the page at the specified physical address by writing to to
+ * the memory mapped address array.
+ * The offset into the memory mapped cache array selects the `color' of the
+ * virtual addresses which will be checked.
+ * Lower two bits of phys control the operation (invalidate/write-back).
+ *
+ * void __flush_cache_4096(unsigned long addr, unsigned long phys,
+ *                         unsigned long exec_offset);
+ *
+ * @addr: address of the memory mapped cache address array
+ * @phys: P1 address to be flushed
+ * @exec_offset: set to 0x20000000 if the flush needs to be executed from P2
+ * (ie from uncached memory), otherwise 0.
+ */
+
+/*
+ * Updated for the 2-way associative cache option on the SH-4-202 and SH7751R.
+ *
+ * The current implmentation simply adds an additional loop to flush the 
+ * other way, but this could be improved by merging both loops to handle the
+ * flushing of boths ways with one iteration.
+ *  
+ * benedict.gaster@superh.com
+ */
+ 
+/*
+ * r4 --- addr
+ * r5 --- phys
+ * r6 --- exec_offset
+ */
+
 ENTRY(__flush_cache_4096)
 	mov.l	1f,r3
 	add	r6,r3
@@ -206,6 +240,9 @@
 	.align	2
 1:	.long	2f
 2:
+#if defined (CONFIG_SH_CACHE_ASSOC)
+	mov     r5, r3
+#endif
 	.rept	32
 	mov.l	r5,@r0
 	mov.l	r5,@(32,r0)
@@ -214,6 +251,21 @@
 	add	r2,r5
 	add	r2,r0
 	.endr
+
+#if defined (CONFIG_SH_CACHE_ASSOC)
+	mov     r4, r0
+	mov     #0x40, r1	! set bit 14 in r0 to imply 2 way.
+	shll8   r1
+	or      r1, r0
+	.rept	32
+	mov.l	r3,@r0
+	mov.l	r3,@(32,r0)
+	mov.l	r3,@(r0,r6)
+	mov.l	r3,@(r0,r7)
+	add	r2,r3
+	add	r2,r0
+	.endr
+#endif
 	nop
 	nop
 	nop
@@ -222,7 +274,8 @@
 	nop
 	nop
 	rts
-	 nop
+	nop
+		
 
 ENTRY(__flush_dcache_all)
 	mov.l	2f,r0
@@ -233,6 +286,46 @@
 	or	r1,r2
 	mov	#32,r3
 	shll2	r3
+
+! TODO : make this be dynamically selected based on CPU probing rather than assembled-in
+
+#if defined (CONFIG_SH_CACHE_ASSOC)
+	mov	#0x40, r5
+	shll8	r5
+	add	r4, r5		! r5 = r4 + 16k
+1:
+	ldc	r2,sr		! set BL bit
+	movca.l	r0,@r4
+	movca.l	r0,@r5
+	ocbi	@r4
+	add	#32,r4
+	ocbi	@r5
+	add	#32,r5
+	movca.l	r0,@r4
+	movca.l	r0,@r5
+	ocbi	@r4
+	add	#32,r4
+	ocbi	@r5
+	add	#32,r5
+	movca.l	r0,@r4
+	movca.l	r0,@r5
+	ocbi	@r4
+	add	#32,r4
+	ocbi	@r5
+	add	#32,r5
+	movca.l	r0,@r4
+	movca.l	r0,@r5
+	ocbi	@r4
+	add	#32, r4
+	ocbi	@r5
+	ldc	r1,sr		! restore SR
+	dt	r3
+	bf/s	1b
+	 add	#32,r5
+
+	rts
+	 nop
+#else
 1:
 	ldc	r2,sr		! set BL bit
 	movca.l	r0,@r4
@@ -253,8 +346,11 @@
 
 	rts
 	 nop
+#endif /* CONFIG_SH_CACHE_ASSOC */
+
 	.align	2
 2:	.long	0xffffc000
+
 3:	.long	SYMBOL_NAME(empty_zero_page)
 4:	.long	0x10000000	! BL bit
 
@@ -268,28 +364,73 @@
 	mov.l	4f,r2
 	or	r1,r2
 	mov	#32,r3
+! TODO : make this be dynamically selected based on CPU probing rather than assembled-in
+
+#if defined (CONFIG_SH_CACHE_ASSOC)
+	mov	#0x40, r5
+	shll8	r5
+	add	r4, r5		! r5 = r4 + 16k
 1:
 	ldc	r2,sr		! set BL bit
 	movca.l	r0,@r4
+	movca.l	r0,@r5
 	ocbi	@r4
 	add	#32,r4
+	ocbi	@r5
+	add	#32,r5
 	movca.l	r0,@r4
+	movca.l	r0,@r5
 	ocbi	@r4
 	add	#32,r4
+	ocbi	@r5
+	add	#32,r5
 	movca.l	r0,@r4
+	movca.l	r0,@r5
 	ocbi	@r4
 	add	#32,r4
+	ocbi	@r5
+	add	#32,r5
 	movca.l	r0,@r4
+	movca.l	r0,@r5
 	ocbi	@r4
+	add	#32,r4
+	ocbi	@r5
+
 	ldc	r1,sr		! restore SR
 	dt	r3
 	bf/s	1b
-	 add	#32,r4
+	add	#32,r5
 
 	rts
-	 nop
+	nop
+#else
+1:
+	ldc	r2,sr		! set BL bit
+	movca.l	r0,@r4
+	ocbi	@r4
+	add	#32,r4
+	
+	movca.l	r0,@r4
+	ocbi	@r4
+	add	#32,r4
+	movca.l	r0,@r4
+	ocbi	@r4
+	add	#32,r4
+	movca.l	r0,@r4
+	ocbi	@r4
+
+	ldc	r1,sr		! restore SR
+	dt	r3
+	bf/s	1b
+	add	#32,r4
+
+	rts
+	nop
+#endif
+
 	.align	2
 2:	.long	0xffffc000
 3:	.long	SYMBOL_NAME(empty_zero_page)
 4:	.long	0x10000000	! BL bit
+	
 #endif
diff -urN linux-2.4.23-pre5/arch/sh64/config.in linux-2.4.23-pre6/arch/sh64/config.in
--- linux-2.4.23-pre5/arch/sh64/config.in	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/arch/sh64/config.in	2003-10-01 10:38:45.000000000 -0700
@@ -54,6 +54,8 @@
 	"2			CONFIG_SH64_PGTABLE_2_LEVEL \
 	 3			CONFIG_SH64_PGTABLE_3_LEVEL" 2
 
+bool 'Fixup misaligned loads/stores occurring in user mode' CONFIG_SH64_USER_MISALIGNED_FIXUP
+
 # Use 32-bit addressing for now.
 # EMI based.
 # P2 (UNCACHED) required to use identity mapping
@@ -300,6 +302,7 @@
 bool 'GDB Stub kernel debug' CONFIG_DEBUG_KERNEL_WITH_GDB_STUB
 bool "Debug: audit page tables on return from syscall/exception/interrupt" CONFIG_SH64_PAGE_TABLE_AUDIT
 dep_bool "Debug: report TLB fill/purge activity through /proc/tlb" CONFIG_SH64_PROC_TLB $CONFIG_PROC_FS
+dep_bool "Debug: report ASIDS through /proc/asids" CONFIG_SH64_PROC_ASIDS $CONFIG_PROC_FS
 
 int 'Kernel messages buffer length shift (0 = default)' CONFIG_LOG_BUF_SHIFT 0
 
diff -urN linux-2.4.23-pre5/arch/sh64/defconfig linux-2.4.23-pre6/arch/sh64/defconfig
--- linux-2.4.23-pre5/arch/sh64/defconfig	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/sh64/defconfig	2003-10-01 10:38:45.000000000 -0700
@@ -39,6 +39,7 @@
 # CONFIG_SH64_FPU_DENORM_FLUSH is not set
 CONFIG_SH64_PGTABLE_2_LEVEL=y
 # CONFIG_SH64_PGTABLE_3_LEVEL is not set
+# CONFIG_SH64_USER_MISALIGNED_FIXUP is not set
 
 #
 # Memory options
@@ -477,6 +478,7 @@
 # CONFIG_DEBUG_KERNEL_WITH_GDB_STUB is not set
 # CONFIG_SH64_PAGE_TABLE_AUDIT is not set
 # CONFIG_SH64_PROC_TLB is not set
+# CONFIG_SH64_PROC_ASIDS is not set
 
 #
 # Library routines
diff -urN linux-2.4.23-pre5/arch/sh64/kernel/fpu.c linux-2.4.23-pre6/arch/sh64/kernel/fpu.c
--- linux-2.4.23-pre5/arch/sh64/kernel/fpu.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/sh64/kernel/fpu.c	2003-10-01 10:38:45.000000000 -0700
@@ -23,47 +23,56 @@
  * has the property that no matter whether considered as single or as
  * double precision, it still represents a signalling NAN.  
  */
-#define sNAN64		0xFFFFFFFFFFFFFFFF
+#define sNAN64		0xFFFFFFFFFFFFFFFFULL
+#define sNAN32		0xFFFFFFFFUL
 
+static union sh_fpu_union init_fpuregs = {
+	.hard = {
+	  .fp_regs = { [0 ... 63] = sNAN32 },
+	  .fpscr = FPSCR_INIT
+	}
+};
+#if 0
 static struct sh_fpu_hard_struct init_fpuregs = {
-	{ [0 ... 31] = sNAN64 },
+	{ [0 ... 63] = sNAN32 },
 	FPSCR_INIT
 };
+#endif
 
 inline void fpsave(struct sh_fpu_hard_struct *fpregs)
 {
-	asm volatile("fst.d     %0, (0*8),"  __d(0)  "\n\t"
-		     "fst.d     %0, (1*8),"  __d(2)  "\n\t"
-		     "fst.d     %0, (2*8),"  __d(4)  "\n\t"
-		     "fst.d     %0, (3*8),"  __d(6)  "\n\t"
-		     "fst.d     %0, (4*8),"  __d(8)  "\n\t"
-		     "fst.d     %0, (5*8),"  __d(10) "\n\t"
-		     "fst.d     %0, (6*8),"  __d(12) "\n\t"
-		     "fst.d     %0, (7*8),"  __d(14) "\n\t"
-		     "fst.d     %0, (8*8),"  __d(16) "\n\t"
-		     "fst.d     %0, (9*8),"  __d(18) "\n\t"
-		     "fst.d     %0, (10*8)," __d(20) "\n\t"
-		     "fst.d     %0, (11*8)," __d(22) "\n\t"
-		     "fst.d     %0, (12*8)," __d(24) "\n\t"
-		     "fst.d     %0, (13*8)," __d(26) "\n\t"
-		     "fst.d     %0, (14*8)," __d(28) "\n\t"
-		     "fst.d     %0, (15*8)," __d(30) "\n\t"
-		     "fst.d     %0, (16*8)," __d(32) "\n\t"
-		     "fst.d     %0, (17*8)," __d(34) "\n\t"
-		     "fst.d     %0, (18*8)," __d(36) "\n\t"
-		     "fst.d     %0, (19*8)," __d(38) "\n\t"
-		     "fst.d     %0, (20*8)," __d(40) "\n\t"
-		     "fst.d     %0, (21*8)," __d(42) "\n\t"
-		     "fst.d     %0, (22*8)," __d(44) "\n\t"
-		     "fst.d     %0, (23*8)," __d(46) "\n\t"
-		     "fst.d     %0, (24*8)," __d(48) "\n\t"
-		     "fst.d     %0, (25*8)," __d(50) "\n\t"
-		     "fst.d     %0, (26*8)," __d(52) "\n\t"
-		     "fst.d     %0, (27*8)," __d(54) "\n\t"
-		     "fst.d     %0, (28*8)," __d(56) "\n\t"
-		     "fst.d     %0, (29*8)," __d(58) "\n\t"
-		     "fst.d     %0, (30*8)," __d(60) "\n\t"
-		     "fst.d     %0, (31*8)," __d(62) "\n\t"
+	asm volatile("fst.p     %0, (0*8),"  __p(0)  "\n\t"
+		     "fst.p     %0, (1*8),"  __p(2)  "\n\t"
+		     "fst.p     %0, (2*8),"  __p(4)  "\n\t"
+		     "fst.p     %0, (3*8),"  __p(6)  "\n\t"
+		     "fst.p     %0, (4*8),"  __p(8)  "\n\t"
+		     "fst.p     %0, (5*8),"  __p(10) "\n\t"
+		     "fst.p     %0, (6*8),"  __p(12) "\n\t"
+		     "fst.p     %0, (7*8),"  __p(14) "\n\t"
+		     "fst.p     %0, (8*8),"  __p(16) "\n\t"
+		     "fst.p     %0, (9*8),"  __p(18) "\n\t"
+		     "fst.p     %0, (10*8)," __p(20) "\n\t"
+		     "fst.p     %0, (11*8)," __p(22) "\n\t"
+		     "fst.p     %0, (12*8)," __p(24) "\n\t"
+		     "fst.p     %0, (13*8)," __p(26) "\n\t"
+		     "fst.p     %0, (14*8)," __p(28) "\n\t"
+		     "fst.p     %0, (15*8)," __p(30) "\n\t"
+		     "fst.p     %0, (16*8)," __p(32) "\n\t"
+		     "fst.p     %0, (17*8)," __p(34) "\n\t"
+		     "fst.p     %0, (18*8)," __p(36) "\n\t"
+		     "fst.p     %0, (19*8)," __p(38) "\n\t"
+		     "fst.p     %0, (20*8)," __p(40) "\n\t"
+		     "fst.p     %0, (21*8)," __p(42) "\n\t"
+		     "fst.p     %0, (22*8)," __p(44) "\n\t"
+		     "fst.p     %0, (23*8)," __p(46) "\n\t"
+		     "fst.p     %0, (24*8)," __p(48) "\n\t"
+		     "fst.p     %0, (25*8)," __p(50) "\n\t"
+		     "fst.p     %0, (26*8)," __p(52) "\n\t"
+		     "fst.p     %0, (27*8)," __p(54) "\n\t"
+		     "fst.p     %0, (28*8)," __p(56) "\n\t"
+		     "fst.p     %0, (29*8)," __p(58) "\n\t"
+		     "fst.p     %0, (30*8)," __p(60) "\n\t"
+		     "fst.p     %0, (31*8)," __p(62) "\n\t"
 
 		     "_fgetscr  " __f(63) 	     "\n\t"
 		     "fst.s     %0, (32*8)," __f(63) "\n\t"
@@ -76,49 +85,49 @@
 static inline void
 fpload(struct sh_fpu_hard_struct *fpregs)
 {
-	asm volatile("fld.d     %0, (0*8),"  __d(0)  "\n\t"
-		     "fld.d     %0, (1*8),"  __d(2)  "\n\t"
-		     "fld.d     %0, (2*8),"  __d(4)  "\n\t"
-		     "fld.d     %0, (3*8),"  __d(6)  "\n\t"
-		     "fld.d     %0, (4*8),"  __d(8)  "\n\t"
-		     "fld.d     %0, (5*8),"  __d(10) "\n\t"
-		     "fld.d     %0, (6*8),"  __d(12) "\n\t"
-		     "fld.d     %0, (7*8),"  __d(14) "\n\t"
-		     "fld.d     %0, (8*8),"  __d(16) "\n\t"
-		     "fld.d     %0, (9*8),"  __d(18) "\n\t"
-		     "fld.d     %0, (10*8)," __d(20) "\n\t"
-		     "fld.d     %0, (11*8)," __d(22) "\n\t"
-		     "fld.d     %0, (12*8)," __d(24) "\n\t"
-		     "fld.d     %0, (13*8)," __d(26) "\n\t"
-		     "fld.d     %0, (14*8)," __d(28) "\n\t"
-		     "fld.d     %0, (15*8)," __d(30) "\n\t"
-		     "fld.d     %0, (16*8)," __d(32) "\n\t"
-		     "fld.d     %0, (17*8)," __d(34) "\n\t"
-		     "fld.d     %0, (18*8)," __d(36) "\n\t"
-		     "fld.d     %0, (19*8)," __d(38) "\n\t"
-		     "fld.d     %0, (20*8)," __d(40) "\n\t"
-		     "fld.d     %0, (21*8)," __d(42) "\n\t"
-		     "fld.d     %0, (22*8)," __d(44) "\n\t"
-		     "fld.d     %0, (23*8)," __d(46) "\n\t"
-		     "fld.d     %0, (24*8)," __d(48) "\n\t"
-		     "fld.d     %0, (25*8)," __d(50) "\n\t"
-		     "fld.d     %0, (26*8)," __d(52) "\n\t"
-		     "fld.d     %0, (27*8)," __d(54) "\n\t"
-		     "fld.d     %0, (28*8)," __d(56) "\n\t"
-		     "fld.d     %0, (29*8)," __d(58) "\n\t"
-		     "fld.d     %0, (30*8)," __d(60) "\n\t"
+	asm volatile("fld.p     %0, (0*8),"  __p(0)  "\n\t"
+		     "fld.p     %0, (1*8),"  __p(2)  "\n\t"
+		     "fld.p     %0, (2*8),"  __p(4)  "\n\t"
+		     "fld.p     %0, (3*8),"  __p(6)  "\n\t"
+		     "fld.p     %0, (4*8),"  __p(8)  "\n\t"
+		     "fld.p     %0, (5*8),"  __p(10) "\n\t"
+		     "fld.p     %0, (6*8),"  __p(12) "\n\t"
+		     "fld.p     %0, (7*8),"  __p(14) "\n\t"
+		     "fld.p     %0, (8*8),"  __p(16) "\n\t"
+		     "fld.p     %0, (9*8),"  __p(18) "\n\t"
+		     "fld.p     %0, (10*8)," __p(20) "\n\t"
+		     "fld.p     %0, (11*8)," __p(22) "\n\t"
+		     "fld.p     %0, (12*8)," __p(24) "\n\t"
+		     "fld.p     %0, (13*8)," __p(26) "\n\t"
+		     "fld.p     %0, (14*8)," __p(28) "\n\t"
+		     "fld.p     %0, (15*8)," __p(30) "\n\t"
+		     "fld.p     %0, (16*8)," __p(32) "\n\t"
+		     "fld.p     %0, (17*8)," __p(34) "\n\t"
+		     "fld.p     %0, (18*8)," __p(36) "\n\t"
+		     "fld.p     %0, (19*8)," __p(38) "\n\t"
+		     "fld.p     %0, (20*8)," __p(40) "\n\t"
+		     "fld.p     %0, (21*8)," __p(42) "\n\t"
+		     "fld.p     %0, (22*8)," __p(44) "\n\t"
+		     "fld.p     %0, (23*8)," __p(46) "\n\t"
+		     "fld.p     %0, (24*8)," __p(48) "\n\t"
+		     "fld.p     %0, (25*8)," __p(50) "\n\t"
+		     "fld.p     %0, (26*8)," __p(52) "\n\t"
+		     "fld.p     %0, (27*8)," __p(54) "\n\t"
+		     "fld.p     %0, (28*8)," __p(56) "\n\t"
+		     "fld.p     %0, (29*8)," __p(58) "\n\t"
+		     "fld.p     %0, (30*8)," __p(60) "\n\t"
 
 		     "fld.s     %0, (32*8)," __f(63) "\n\t"
 		     "_fputscr  " __f(63) 	     "\n\t"
 
-	     	     "fld.d     %0, (31*8)," __d(62) "\n\t"
+	     	     "fld.p     %0, (31*8)," __p(62) "\n\t"
 		: /* no output */
 		: "r" (fpregs) );
 }
 
 void fpinit(struct sh_fpu_hard_struct *fpregs)
 {
-	*fpregs = init_fpuregs;
+	*fpregs = init_fpuregs.hard;
 }
 
 asmlinkage void
@@ -163,7 +172,7 @@
                 fpload(&current->thread.fpu.hard);
         } else {
 		/* First time FPU user.  */
-		fpload(&init_fpuregs);
+		fpload(&init_fpuregs.hard);
                 current->used_math = 1;
         }
 	release_fpu();
diff -urN linux-2.4.23-pre5/arch/sh64/kernel/process.c linux-2.4.23-pre6/arch/sh64/kernel/process.c
--- linux-2.4.23-pre5/arch/sh64/kernel/process.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/sh64/kernel/process.c	2003-10-01 10:38:45.000000000 -0700
@@ -937,3 +937,48 @@
 	return pc;
 }
 
+/* Provide a /proc/asids file that lists out the
+   ASIDs currently associated with the processes.  (If the DM.PC register is
+   examined through the debug link, this shows ASID + PC.  To make use of this,
+   the PID->ASID relationship needs to be known.  This is primarily for
+   debugging.)
+   */
+
+#if defined(CONFIG_SH64_PROC_ASIDS)
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+
+static int
+asids_proc_info(char *buf, char **start, off_t fpos, int length, int *eof, void *data)
+{
+	int len=0;
+	struct task_struct *p;
+	read_lock(&tasklist_lock);
+	for_each_task(p) {
+		int pid = p->pid;
+		struct mm_struct *mm;
+		if (!pid) continue;
+		mm = p->mm;
+		if (mm) {
+			unsigned long asid, context;
+			context = mm->context;
+			asid = (context & 0xff);
+			len += sprintf(buf+len, "%5d : %02x\n", pid, asid);
+		} else {
+			len += sprintf(buf+len, "%5d : (none)\n", pid);
+		}
+	}
+	read_unlock(&tasklist_lock);
+	*eof = 1;
+	return len;
+}
+
+static int __init register_proc_asids(void)
+{
+  create_proc_read_entry("asids", 0, NULL, asids_proc_info, NULL);
+  return 0;
+}
+
+__initcall(register_proc_asids);
+#endif
+
diff -urN linux-2.4.23-pre5/arch/sh64/kernel/traps.c linux-2.4.23-pre6/arch/sh64/kernel/traps.c
--- linux-2.4.23-pre5/arch/sh64/kernel/traps.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/sh64/kernel/traps.c	2003-10-01 10:38:45.000000000 -0700
@@ -7,6 +7,7 @@
  *
  * Copyright (C) 2000, 2001  Paolo Alberelli
  * Copyright (C) 2003  Paul Mundt
+ * Copyright (C) 2003  Richard Curnow
  *
  */
 
@@ -27,11 +28,15 @@
 #include <linux/delay.h>
 #include <linux/spinlock.h>
 
+#include <linux/interrupt.h>
+#include <linux/sysctl.h>
+
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/atomic.h>
 #include <asm/processor.h>
+#include <asm/pgtable.h>
 
 #undef DEBUG_EXCEPTION
 #ifdef DEBUG_EXCEPTION
@@ -42,14 +47,13 @@
 #define show_excp_regs(a, b, c, d)
 #endif
 
+static void do_unhandled_exception(int trapnr, int signr, char *str, char *fn_name,
+		unsigned long error_code, struct pt_regs *regs, struct task_struct *tsk);
+
 #define DO_ERROR(trapnr, signr, str, name, tsk) \
 asmlinkage void do_##name(unsigned long error_code, struct pt_regs *regs) \
 { \
-	show_excp_regs(__FUNCTION__, trapnr, signr, regs); \
-	tsk->thread.error_code = error_code; \
-	tsk->thread.trap_no = trapnr; \
-	if (user_mode(regs)) force_sig(signr, tsk); \
-	die_if_no_fixup(str,regs,error_code); \
+	do_unhandled_exception(trapnr, signr, str, __FUNCTION__, error_code, regs, current); \
 }
 
 spinlock_t die_lock;
@@ -84,11 +88,34 @@
 	}
 }
 
-DO_ERROR( 7, SIGSEGV, "address error (load)", address_error_load, current)
-DO_ERROR( 8, SIGSEGV, "address error (store)", address_error_store, current)
 DO_ERROR(13, SIGILL,  "illegal slot instruction", illegal_slot_inst, current)
 DO_ERROR(87, SIGSEGV, "address error (exec)", address_error_exec, current)
 
+
+/* Implement misaligned load/store handling for kernel (and optionally for user
+   mode too).  Limitation : only SHmedia mode code is handled - there is no
+   handling at all for misaligned accesses occurring in SHcompact code yet. */
+
+static int misaligned_fixup(struct pt_regs *regs);
+
+asmlinkage void do_address_error_load(unsigned long error_code, struct pt_regs *regs)
+{
+	if (misaligned_fixup(regs) < 0) {
+		do_unhandled_exception(7, SIGSEGV, "address error(load)", __FUNCTION__,
+				error_code, regs, current);
+	}
+	return;
+}
+
+asmlinkage void do_address_error_store(unsigned long error_code, struct pt_regs *regs)
+{
+	if (misaligned_fixup(regs) < 0) {
+		do_unhandled_exception(8, SIGSEGV, "address error(store)", __FUNCTION__,
+				error_code, regs, current);
+	}
+	return;
+}
+
 #if defined(CONFIG_SH64_ID2815_WORKAROUND)
 
 #define OPCODE_INVALID      0
@@ -121,7 +148,7 @@
 	   which should take ITLBMISS or EXECPROT exceptions at the target
 	   falsely take RESINST at the target instead. */
 
-	unsigned long opcode;
+	unsigned long opcode = 0x6ff4fff0; /* guaranteed reserved opcode */
 	unsigned long pc, aligned_pc;
 	int get_user_error;
 	int trapnr = 12;
@@ -203,11 +230,7 @@
 		}
 	}
 
-	show_excp_regs("do_reserved_inst", trapnr, signr, regs);
-	current->thread.error_code = error_code;
-	current->thread.trap_no = trapnr;
-	if (user_mode(regs)) force_sig(signr, current);
-	die_if_no_fixup(exception_name, regs, error_code);
+	do_unhandled_exception(trapnr, signr, exception_name, "do_reserved_inst", error_code, regs, current);
 }
 
 #else /* CONFIG_SH64_ID2815_WORKAROUND */
@@ -302,3 +325,640 @@
 	show_task(NULL);
 }
 
+static void do_unhandled_exception(int trapnr, int signr, char *str, char *fn_name,
+		unsigned long error_code, struct pt_regs *regs, struct task_struct *tsk)
+{
+	show_excp_regs(fn_name, trapnr, signr, regs);
+	tsk->thread.error_code = error_code;
+	tsk->thread.trap_no = trapnr;
+	if (user_mode(regs)) force_sig(signr, tsk);
+	die_if_no_fixup(str, regs, error_code);
+}
+
+static int read_opcode(unsigned long long pc, unsigned long *result_opcode)
+{
+	int get_user_error;
+	unsigned long aligned_pc;
+	unsigned long opcode;
+
+	if ((pc & 3) == 1) {
+		/* SHmedia */
+		aligned_pc = pc & ~3;
+		if (!access_ok(VERIFY_READ, aligned_pc, sizeof(unsigned long))) {
+			get_user_error = -EFAULT;
+		} else {
+			get_user_error = __get_user(opcode, (unsigned long *)aligned_pc);
+			*result_opcode = opcode;
+		}
+		return get_user_error;
+	} else if ((pc & 1) == 0) {
+		/* SHcompact */
+		/* TODO : provide handling for this. */
+		return -EFAULT;
+	} else {
+		/* misaligned */
+		return -EFAULT;
+	}
+}
+
+static int address_is_sign_extended(__u64 a)
+{
+	__u64 b;
+#if (NEFF == 32)	
+	b = (__u64)(__s64)(__s32)(a & 0xffffffffUL);
+	return (b == a) ? 1 : 0;
+#else
+#error "Sign extend check only works for NEFF==32"
+#endif
+}
+
+static int generate_and_check_address(struct pt_regs *regs,
+				      __u32 opcode,
+				      int displacement_not_indexed,
+				      int width_shift,
+				      __u64 *address)
+{
+	/* return -1 for fault, 0 for OK */
+
+	__u64 base_address, addr;
+	int basereg;
+	int do_as_user = user_mode(regs);
+	
+	basereg = (opcode >> 20) & 0x3f;
+	base_address = regs->regs[basereg];
+	if (displacement_not_indexed) {
+		__s64 displacement;
+		displacement = (opcode >> 10) & 0x3ff;
+		displacement = ((displacement << 54) >> 54); /* sign extend */
+		addr = (__u64)((__s64)base_address + (displacement << width_shift));
+	} else {
+		__u64 offset;
+		int offsetreg;
+		offsetreg = (opcode >> 10) & 0x3f;
+		offset = regs->regs[offsetreg];
+		addr = base_address + offset;
+	}
+
+	/* Check sign extended */
+	if (!address_is_sign_extended(addr)) {
+		return -1;
+	}
+	
+#if defined(CONFIG_SH64_USER_MISALIGNED_FIXUP)
+	/* Check accessible.  For misaligned access in the kernel, assume the
+	   address is always accessible (and if not, just fault when the
+	   load/store gets done.) */
+	if (do_as_user) {
+		if (addr >= TASK_SIZE) {
+			return -1;
+		}
+		/* Do access_ok check later - it depends on whether it's a load or a store. */
+	}
+#endif
+
+	*address = addr;
+	return 0;
+}
+
+/* Default value as for sh */
+#if defined(CONFIG_SH64_USER_MISALIGNED_FIXUP)
+static int user_mode_unaligned_fixup_count = 10;
+static int user_mode_unaligned_fixup_enable = 1;
+#endif
+
+static int kernel_mode_unaligned_fixup_count = 32;
+
+static void misaligned_kernel_word_load(__u64 address, int do_sign_extend, __u64 *result)
+{
+	unsigned short x;
+	unsigned char *p, *q;
+	p = (unsigned char *) (int) address;
+	q = (unsigned char *) &x;
+	q[0] = p[0];
+	q[1] = p[1];
+
+	if (do_sign_extend) {
+		*result = (__u64)(__s64) *(short *) &x;
+	} else {
+		*result = (__u64) x;
+	}
+}
+
+static void misaligned_kernel_word_store(__u64 address, __u64 value)
+{
+	unsigned short x;
+	unsigned char *p, *q;
+	p = (unsigned char *) (int) address;
+	q = (unsigned char *) &x;
+
+	x = (__u16) value;
+	p[0] = q[0];
+	p[1] = q[1];
+}
+
+static int misaligned_load(struct pt_regs *regs,
+			   __u32 opcode,
+			   int displacement_not_indexed,
+			   int width_shift,
+			   int do_sign_extend)
+{
+	/* Return -1 for a fault, 0 for OK */
+	int error;
+	int destreg;
+	__u64 address;
+
+	error = generate_and_check_address(regs, opcode, 
+			displacement_not_indexed, width_shift, &address);
+	if (error < 0) {
+		return error;
+	}
+
+	destreg = (opcode >> 4) & 0x3f;
+#if defined(CONFIG_SH64_USER_MISALIGNED_FIXUP)
+	if (user_mode(regs)) {
+		__u64 buffer;
+		
+		if (!access_ok(VERIFY_READ, (unsigned long) address, 1UL<<width_shift)) {
+			return -1;
+		}
+
+		if (__copy_user(&buffer, (const void *)(int)address, (1 << width_shift)) > 0) {
+			return -1; /* fault */
+		}
+		switch (width_shift) {
+		case 1:
+			if (do_sign_extend) {
+				regs->regs[destreg] = (__u64)(__s64) *(__s16 *) &buffer;
+			} else {
+				regs->regs[destreg] = (__u64) *(__u16 *) &buffer;
+			}
+			break;
+		case 2:
+			regs->regs[destreg] = (__u64)(__s64) *(__s32 *) &buffer;
+			break;
+		case 3:
+			regs->regs[destreg] = buffer;
+			break;
+		default:
+			printk("Unexpected width_shift %d in misaligned_load, PC=%08lx\n", 
+				width_shift, (unsigned long) regs->pc);
+			break;
+		}
+	} else
+#endif
+	{
+		/* kernel mode - we can take short cuts since if we fault, it's a genuine bug */
+		__u64 lo, hi;
+
+		switch (width_shift) {
+		case 1:
+			misaligned_kernel_word_load(address, do_sign_extend, &regs->regs[destreg]);
+			break;
+		case 2:
+			asm ("ldlo.l %1, 0, %0" : "=r" (lo) : "r" (address));
+			asm ("ldhi.l %1, 3, %0" : "=r" (hi) : "r" (address));
+			regs->regs[destreg] = lo | hi;
+			break;
+		case 3:
+			asm ("ldlo.q %1, 0, %0" : "=r" (lo) : "r" (address));
+			asm ("ldhi.q %1, 7, %0" : "=r" (hi) : "r" (address));
+			regs->regs[destreg] = lo | hi;
+			break;
+
+		default:
+			printk("Unexpected width_shift %d in misaligned_load, PC=%08lx\n", 
+				width_shift, (unsigned long) regs->pc);
+			break;
+		}
+	}
+
+	return 0;
+
+}
+
+static int misaligned_store(struct pt_regs *regs,
+			    __u32 opcode,
+			    int displacement_not_indexed,
+			    int width_shift)
+{
+	/* Return -1 for a fault, 0 for OK */
+	int error;
+	int srcreg;
+	__u64 address;
+
+	error = generate_and_check_address(regs, opcode, 
+			displacement_not_indexed, width_shift, &address);
+	if (error < 0) {
+		return error;
+	}
+
+	srcreg = (opcode >> 4) & 0x3f;
+#if defined(CONFIG_SH64_USER_MISALIGNED_FIXUP)
+	if (user_mode(regs)) {
+		__u64 buffer;
+		
+		if (!access_ok(VERIFY_WRITE, (unsigned long) address, 1UL<<width_shift)) {
+			return -1;
+		}
+
+		switch (width_shift) {
+		case 1:
+			*(__u16 *) &buffer = (__u16) regs->regs[srcreg];
+			break;
+		case 2:
+			*(__u32 *) &buffer = (__u32) regs->regs[srcreg];
+			break;
+		case 3:
+			buffer = regs->regs[srcreg];
+			break;
+		default:
+			printk("Unexpected width_shift %d in misaligned_store, PC=%08lx\n", 
+				width_shift, (unsigned long) regs->pc);
+			break;
+		}
+
+		if (__copy_user((void *)(int)address, &buffer, (1 << width_shift)) > 0) {
+			return -1; /* fault */
+		}
+	} else
+#endif
+	{
+		/* kernel mode - we can take short cuts since if we fault, it's a genuine bug */
+		__u64 val = regs->regs[srcreg];
+
+		switch (width_shift) {
+		case 1:
+			misaligned_kernel_word_store(address, val);
+			break;
+		case 2:
+			asm ("stlo.l %1, 0, %0" : : "r" (val), "r" (address));
+			asm ("sthi.l %1, 3, %0" : : "r" (val), "r" (address));
+			break;
+		case 3:
+			asm ("stlo.q %1, 0, %0" : : "r" (val), "r" (address));
+			asm ("sthi.q %1, 7, %0" : : "r" (val), "r" (address));
+			break;
+
+		default:
+			printk("Unexpected width_shift %d in misaligned_store, PC=%08lx\n", 
+				width_shift, (unsigned long) regs->pc);
+			break;
+		}
+	}
+
+	return 0;
+
+}
+
+#if defined(CONFIG_SH64_USER_MISALIGNED_FIXUP)
+/* Never need to fix up misaligned FPU accesses within the kernel since that's a real
+   error. */
+static int misaligned_fpu_load(struct pt_regs *regs,
+			   __u32 opcode,
+			   int displacement_not_indexed,
+			   int width_shift,
+			   int do_paired_load)
+{
+	/* Return -1 for a fault, 0 for OK */
+	int error;
+	int destreg;
+	__u64 address;
+
+	error = generate_and_check_address(regs, opcode, 
+			displacement_not_indexed, width_shift, &address);
+	if (error < 0) {
+		return error;
+	}
+
+	destreg = (opcode >> 4) & 0x3f;
+	if (user_mode(regs)) {
+		__u64 buffer;
+		__u32 buflo, bufhi;
+		
+		if (!access_ok(VERIFY_READ, (unsigned long) address, 1UL<<width_shift)) {
+			return -1;
+		}
+
+		if (__copy_user(&buffer, (const void *)(int)address, (1 << width_shift)) > 0) {
+			return -1; /* fault */
+		}
+		/* 'current' may be the current owner of the FPU state, so
+		   context switch the registers into memory so they can be
+		   indexed by register number. */
+		if (last_task_used_math == current) {
+			grab_fpu();
+			fpsave(&current->thread.fpu.hard);
+			release_fpu();
+			last_task_used_math = NULL;
+			regs->sr |= SR_FD;
+		}
+
+		buflo = *(__u32*) &buffer;
+		bufhi = *(1 + (__u32*) &buffer);
+
+		switch (width_shift) {
+		case 2:
+			current->thread.fpu.hard.fp_regs[destreg] = buflo;
+			break;
+		case 3:
+			if (do_paired_load) {
+				current->thread.fpu.hard.fp_regs[destreg] = buflo;
+				current->thread.fpu.hard.fp_regs[destreg+1] = bufhi;
+			} else {
+#if defined(CONFIG_LITTLE_ENDIAN)
+				current->thread.fpu.hard.fp_regs[destreg] = bufhi;
+				current->thread.fpu.hard.fp_regs[destreg+1] = buflo;
+#else
+				current->thread.fpu.hard.fp_regs[destreg] = buflo;
+				current->thread.fpu.hard.fp_regs[destreg+1] = bufhi;
+#endif
+			}
+			break;
+		default:
+			printk("Unexpected width_shift %d in misaligned_fpu_load, PC=%08lx\n", 
+				width_shift, (unsigned long) regs->pc);
+			break;
+		}
+		return 0;
+	} else {
+		die ("Misaligned FPU load inside kernel", regs, 0);
+		return -1;
+	}
+
+
+}
+
+static int misaligned_fpu_store(struct pt_regs *regs,
+			   __u32 opcode,
+			   int displacement_not_indexed,
+			   int width_shift,
+			   int do_paired_load)
+{
+	/* Return -1 for a fault, 0 for OK */
+	int error;
+	int srcreg;
+	__u64 address;
+
+	error = generate_and_check_address(regs, opcode, 
+			displacement_not_indexed, width_shift, &address);
+	if (error < 0) {
+		return error;
+	}
+
+	srcreg = (opcode >> 4) & 0x3f;
+	if (user_mode(regs)) {
+		__u64 buffer;
+		/* Initialise these to NaNs. */
+		__u32 buflo=0xffffffffUL, bufhi=0xffffffffUL;
+		
+		if (!access_ok(VERIFY_WRITE, (unsigned long) address, 1UL<<width_shift)) {
+			return -1;
+		}
+
+		/* 'current' may be the current owner of the FPU state, so
+		   context switch the registers into memory so they can be
+		   indexed by register number. */
+		if (last_task_used_math == current) {
+			grab_fpu();
+			fpsave(&current->thread.fpu.hard);
+			release_fpu();
+			last_task_used_math = NULL;
+			regs->sr |= SR_FD;
+		}
+
+		switch (width_shift) {
+		case 2:
+			buflo = current->thread.fpu.hard.fp_regs[srcreg];
+			break;
+		case 3:
+			if (do_paired_load) {
+				buflo = current->thread.fpu.hard.fp_regs[srcreg];
+				bufhi = current->thread.fpu.hard.fp_regs[srcreg+1];
+			} else {
+#if defined(CONFIG_LITTLE_ENDIAN)
+				bufhi = current->thread.fpu.hard.fp_regs[srcreg];
+				buflo = current->thread.fpu.hard.fp_regs[srcreg+1];
+#else
+				buflo = current->thread.fpu.hard.fp_regs[srcreg];
+				bufhi = current->thread.fpu.hard.fp_regs[srcreg+1];
+#endif
+			}
+			break;
+		default:
+			printk("Unexpected width_shift %d in misaligned_fpu_store, PC=%08lx\n", 
+				width_shift, (unsigned long) regs->pc);
+			break;
+		}
+		
+		*(__u32*) &buffer = buflo;
+		*(1 + (__u32*) &buffer) = bufhi;
+		if (__copy_user((void *)(int)address, &buffer, (1 << width_shift)) > 0) {
+			return -1; /* fault */
+		}
+		return 0;
+	} else {
+		die ("Misaligned FPU load inside kernel", regs, 0);
+		return -1;
+	}
+}
+#endif
+
+static int misaligned_fixup(struct pt_regs *regs)
+{
+	unsigned long opcode;
+	int error;
+	int major, minor;
+
+#if !defined(CONFIG_SH64_USER_MISALIGNED_FIXUP)
+	/* Never fixup user mode misaligned accesses without this option enabled. */
+	return -1;
+#else
+	if (!user_mode_unaligned_fixup_enable) return -1;
+#endif
+
+	error = read_opcode(regs->pc, &opcode);
+	if (error < 0) {
+		return error;
+	}
+	major = (opcode >> 26) & 0x3f;
+	minor = (opcode >> 16) & 0xf;
+
+#if defined(CONFIG_SH64_USER_MISALIGNED_FIXUP)
+	if (user_mode(regs) && (user_mode_unaligned_fixup_count > 0)) {
+		--user_mode_unaligned_fixup_count;
+		/* Only do 'count' worth of these reports, to remove a potential DoS against syslog */
+		printk("Fixing up unaligned userspace access in \"%s\" pid=%d pc=0x%08x ins=0x%08lx\n",
+		       current->comm, current->pid, (__u32)regs->pc, opcode);
+	} else
+#endif
+	if (!user_mode(regs) && (kernel_mode_unaligned_fixup_count > 0)) {
+		--kernel_mode_unaligned_fixup_count;
+		if (in_interrupt()) {
+			printk("Fixing up unaligned kernelspace access in interrupt pc=0x%08x ins=0x%08lx\n",
+			       (__u32)regs->pc, opcode);
+		} else {
+			printk("Fixing up unaligned kernelspace access in \"%s\" pid=%d pc=0x%08x ins=0x%08lx\n",
+			       current->comm, current->pid, (__u32)regs->pc, opcode);
+		}
+	}
+
+	
+	switch (major) {
+		case (0x84>>2): /* LD.W */
+			error = misaligned_load(regs, opcode, 1, 1, 1);
+			break;
+		case (0xb0>>2): /* LD.UW */
+			error = misaligned_load(regs, opcode, 1, 1, 0);
+			break;
+		case (0x88>>2): /* LD.L */
+			error = misaligned_load(regs, opcode, 1, 2, 1);
+			break;
+		case (0x8c>>2): /* LD.Q */
+			error = misaligned_load(regs, opcode, 1, 3, 0);
+			break;
+
+		case (0xa4>>2): /* ST.W */
+			error = misaligned_store(regs, opcode, 1, 1);
+			break;
+		case (0xa8>>2): /* ST.L */
+			error = misaligned_store(regs, opcode, 1, 2);
+			break;
+		case (0xac>>2): /* ST.Q */
+			error = misaligned_store(regs, opcode, 1, 3);
+			break;
+
+		case (0x40>>2): /* indexed loads */
+			switch (minor) {
+				case 0x1: /* LDX.W */
+					error = misaligned_load(regs, opcode, 0, 1, 1);
+					break;
+				case 0x5: /* LDX.UW */
+					error = misaligned_load(regs, opcode, 0, 1, 0);
+					break;
+				case 0x2: /* LDX.L */
+					error = misaligned_load(regs, opcode, 0, 2, 1);
+					break;
+				case 0x3: /* LDX.Q */
+					error = misaligned_load(regs, opcode, 0, 3, 0);
+					break;
+				default:
+					error = -1;
+					break;
+			}
+			break;
+
+		case (0x60>>2): /* indexed stores */
+			switch (minor) {
+				case 0x1: /* STX.W */
+					error = misaligned_store(regs, opcode, 0, 1);
+					break;
+				case 0x2: /* STX.L */
+					error = misaligned_store(regs, opcode, 0, 2);
+					break;
+				case 0x3: /* STX.Q */
+					error = misaligned_store(regs, opcode, 0, 3);
+					break;
+				default:
+					error = -1;
+					break;
+			}
+			break;
+
+#if defined(CONFIG_SH64_USER_MISALIGNED_FIXUP)
+		case (0x94>>2): /* FLD.S */
+			error = misaligned_fpu_load(regs, opcode, 1, 2, 0);
+			break;
+		case (0x98>>2): /* FLD.P */
+			error = misaligned_fpu_load(regs, opcode, 1, 3, 1);
+			break;
+		case (0x9c>>2): /* FLD.D */
+			error = misaligned_fpu_load(regs, opcode, 1, 3, 0);
+			break;
+		case (0x1c>>2): /* floating indexed loads */
+			switch (minor) {
+			case 0x8: /* FLDX.S */
+				error = misaligned_fpu_load(regs, opcode, 0, 2, 0);
+				break;
+			case 0xd: /* FLDX.P */
+				error = misaligned_fpu_load(regs, opcode, 0, 3, 1);
+				break;
+			case 0x9: /* FLDX.D */
+				error = misaligned_fpu_load(regs, opcode, 0, 3, 0);
+				break;
+			default:
+				error = -1;
+				break;
+			}
+			break;
+		case (0xb4>>2): /* FLD.S */
+			error = misaligned_fpu_store(regs, opcode, 1, 2, 0);
+			break;
+		case (0xb8>>2): /* FLD.P */
+			error = misaligned_fpu_store(regs, opcode, 1, 3, 1);
+			break;
+		case (0xbc>>2): /* FLD.D */
+			error = misaligned_fpu_store(regs, opcode, 1, 3, 0);
+			break;
+		case (0x3c>>2): /* floating indexed stores */
+			switch (minor) {
+			case 0x8: /* FSTX.S */
+				error = misaligned_fpu_store(regs, opcode, 0, 2, 0);
+				break;
+			case 0xd: /* FSTX.P */
+				error = misaligned_fpu_store(regs, opcode, 0, 3, 1);
+				break;
+			case 0x9: /* FSTX.D */
+				error = misaligned_fpu_store(regs, opcode, 0, 3, 0);
+				break;
+			default:
+				error = -1;
+				break;
+			}
+			break;
+#endif
+
+		default:
+			/* Fault */
+			error = -1;
+			break;
+	}
+
+	if (error < 0) {
+		return error;
+	} else {
+		regs->pc += 4; /* Skip the instruction that's just been emulated */
+		return 0;
+	}
+
+}
+
+static ctl_table unaligned_table[] = {
+	{1, "kernel_reports", &kernel_mode_unaligned_fixup_count,
+		sizeof(int), 0644, NULL, &proc_dointvec},
+#if defined(CONFIG_SH64_USER_MISALIGNED_FIXUP)
+	{2, "user_reports", &user_mode_unaligned_fixup_count,
+		sizeof(int), 0644, NULL, &proc_dointvec},
+	{3, "user_enable", &user_mode_unaligned_fixup_enable,
+		sizeof(int), 0644, NULL, &proc_dointvec},
+#endif
+	{0}
+};
+
+static ctl_table unaligned_root[] = {
+	{1, "unaligned_fixup", NULL, 0, 0555, unaligned_table},
+	{0}
+};
+
+static ctl_table sh64_root[] = {
+	{1, "sh64", NULL, 0, 0555, unaligned_root},
+	{0}
+};
+static struct ctl_table_header *sysctl_header;
+static int __init init_sysctl(void)
+{
+	sysctl_header = register_sysctl_table(sh64_root, 0);
+	return 0;
+}
+
+__initcall(init_sysctl);
+
diff -urN linux-2.4.23-pre5/arch/x86_64/Makefile linux-2.4.23-pre6/arch/x86_64/Makefile
--- linux-2.4.23-pre5/arch/x86_64/Makefile	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/Makefile	2003-10-01 10:38:45.000000000 -0700
@@ -19,7 +19,7 @@
 # 20010105  Andi Kleen, add IA32 compiler.
 #           ....and later removed it again....
 #
-# $Id: Makefile,v 1.33 2003/05/12 14:36:34 ak Exp $
+# $Id: Makefile,v 1.34 2003/09/25 02:50:05 ak Exp $
 
 #
 # early bootup linking needs 32bit. You can either use real 32bit tools
@@ -38,6 +38,8 @@
 LDFLAGS=-e stext
 LINKFLAGS =-T $(TOPDIR)/arch/x86_64/vmlinux.lds $(LDFLAGS)
 
+check_gcc = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null > /dev/null 2>&1 ; then echo "$(1)"; else echo "$(2)"; fi)
+
 CFLAGS += -mno-red-zone
 CFLAGS += -mcmodel=kernel
 CFLAGS += -pipe
@@ -51,9 +53,7 @@
 ifneq ($(CONFIG_X86_REMOTE_DEBUG),y)
 CFLAGS += -fno-asynchronous-unwind-tables
 endif
-
-# prevent gcc from keeping the stack 16 byte aligned (FIXME)
-#CFLAGS += -mpreferred-stack-boundary=2
+CFLAGS += $(call check_gcc,-fno-unit-at-a-time,)
 
 HEAD := arch/x86_64/kernel/head.o arch/x86_64/kernel/head64.o arch/x86_64/kernel/init_task.o
 
diff -urN linux-2.4.23-pre5/arch/x86_64/boot/setup.S linux-2.4.23-pre6/arch/x86_64/boot/setup.S
--- linux-2.4.23-pre5/arch/x86_64/boot/setup.S	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/boot/setup.S	2003-10-01 10:38:45.000000000 -0700
@@ -524,7 +524,7 @@
 	cmpw	$0, %cs:realmode_swtch
 	jz	rmodeswtch_normal
 
-	lcall	%cs:realmode_swtch
+	lcall	*%cs:realmode_swtch
 
 	jmp	rmodeswtch_end
 
diff -urN linux-2.4.23-pre5/arch/x86_64/config.in linux-2.4.23-pre6/arch/x86_64/config.in
--- linux-2.4.23-pre5/arch/x86_64/config.in	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/config.in	2003-10-01 10:38:45.000000000 -0700
@@ -10,7 +10,7 @@
 define_bool CONFIG_ISA n
 define_bool CONFIG_SBUS n
 
-define_bool CONFIG_UID16 y
+define_bool CONFIG_UID16 n
 define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y
 define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n
 define_bool CONFIG_X86_CMPXCHG y
@@ -66,7 +66,7 @@
    define_bool CONFIG_X86_UP_IOAPIC y
 else
    define_bool CONFIG_HAVE_DEC_LOCK y
-   int  'Maximum number of CPUs (2-32)' CONFIG_NR_CPUS 32
+   int  'Maximum number of CPUs (2-8)' CONFIG_NR_CPUS 4
 fi
 
 bool 'Machine check support' CONFIG_MCE
diff -urN linux-2.4.23-pre5/arch/x86_64/defconfig linux-2.4.23-pre6/arch/x86_64/defconfig
--- linux-2.4.23-pre5/arch/x86_64/defconfig	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/defconfig	2003-10-01 10:38:45.000000000 -0700
@@ -40,13 +40,15 @@
 CONFIG_X86_IO_APIC=y
 CONFIG_X86_LOCAL_APIC=y
 CONFIG_MTRR=y
-# CONFIG_SMP is not set
-CONFIG_NR_CPUS=32
+CONFIG_SMP=y
 CONFIG_HPET_TIMER=y
 CONFIG_GART_IOMMU=y
-CONFIG_X86_UP_IOAPIC=y
+CONFIG_HAVE_DEC_LOCK=y
+CONFIG_NR_CPUS=4
 CONFIG_MCE=y
-# CONFIG_K8_NUMA is not set
+CONFIG_K8_NUMA=y
+CONFIG_DISCONTIGMEM=y
+CONFIG_NUMA=y
 
 #
 # General setup
@@ -89,6 +91,7 @@
 # CONFIG_ACPI_ASUS is not set
 # CONFIG_ACPI_TOSHIBA is not set
 CONFIG_ACPI_DEBUG=y
+# CONFIG_ACPI_RELAXED_AML is not set
 
 #
 # Memory Technology Devices (MTD)
@@ -109,6 +112,7 @@
 # CONFIG_BLK_CPQ_DA is not set
 # CONFIG_BLK_CPQ_CISS_DA is not set
 # CONFIG_CISS_SCSI_TAPE is not set
+# CONFIG_CISS_MONITOR_THREAD is not set
 # CONFIG_BLK_DEV_DAC960 is not set
 # CONFIG_BLK_DEV_UMEM is not set
 CONFIG_BLK_DEV_LOOP=y
@@ -307,6 +311,9 @@
 CONFIG_AIC7XXX_RESET_DELAY_MS=15000
 # CONFIG_AIC7XXX_PROBE_EISA_VL is not set
 # CONFIG_AIC7XXX_BUILD_FIRMWARE is not set
+# CONFIG_AIC7XXX_DEBUG_ENABLE is not set
+CONFIG_AIC7XXX_DEBUG_MASK=0
+# CONFIG_AIC7XXX_REG_PRETTY_PRINT is not set
 # CONFIG_SCSI_AIC79XX is not set
 # CONFIG_SCSI_ADVANSYS is not set
 # CONFIG_SCSI_IN2000 is not set
@@ -351,8 +358,9 @@
 #
 # Fusion MPT device support
 #
-# CONFIG_FUSION is not set
-# CONFIG_FUSION_BOOT is not set
+CONFIG_FUSION=y
+CONFIG_FUSION_BOOT=y
+CONFIG_FUSION_MAX_SGE=40
 # CONFIG_FUSION_ISENSE is not set
 # CONFIG_FUSION_CTL is not set
 # CONFIG_FUSION_LAN is not set
@@ -517,6 +525,7 @@
 # CONFIG_SCx200_GPIO is not set
 # CONFIG_AMD_RNG is not set
 # CONFIG_INTEL_RNG is not set
+CONFIG_HW_RANDOM=y
 # CONFIG_AMD_PM768 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
@@ -529,7 +538,7 @@
 #
 # CONFIG_FTAPE is not set
 CONFIG_AGP=y
-CONFIG_AGP_AMD_8151=y
+CONFIG_AGP_AMD_K8=y
 CONFIG_AGP_INTEL=y
 CONFIG_AGP_I810=y
 CONFIG_AGP_VIA=y
@@ -537,6 +546,12 @@
 CONFIG_AGP_SIS=y
 CONFIG_AGP_ALI=y
 CONFIG_AGP_SWORKS=y
+CONFIG_AGP_NVIDIA=y
+# CONFIG_AGP_ATI is not set
+
+#
+# Direct Rendering Manager (XFree86 DRI support)
+#
 # CONFIG_DRM is not set
 # CONFIG_MWAVE is not set
 
@@ -549,6 +564,7 @@
 # File systems
 #
 # CONFIG_QUOTA is not set
+# CONFIG_QFMT_V2 is not set
 CONFIG_AUTOFS_FS=y
 # CONFIG_AUTOFS4_FS is not set
 CONFIG_REISERFS_FS=y
@@ -558,6 +574,7 @@
 # CONFIG_ADFS_FS_RW 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_BEFS_DEBUG is not set
 # CONFIG_BFS_FS is not set
@@ -599,6 +616,7 @@
 # CONFIG_UDF_RW is not set
 # CONFIG_UFS_FS is not set
 # CONFIG_UFS_FS_WRITE is not set
+# CONFIG_SIMICSFS is not set
 
 #
 # Network File Systems
@@ -607,6 +625,7 @@
 # CONFIG_INTERMEZZO_FS is not set
 CONFIG_NFS_FS=y
 CONFIG_NFS_V3=y
+# CONFIG_NFS_DIRECTIO is not set
 # CONFIG_ROOT_NFS is not set
 CONFIG_NFSD=y
 CONFIG_NFSD_V3=y
@@ -704,17 +723,23 @@
 # CONFIG_BLUEZ is not set
 
 #
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
+
+#
 # Kernel hacking
 #
 CONFIG_DEBUG_KERNEL=y
-# CONFIG_DEBUG_SLAB is not set
+CONFIG_DEBUG_SLAB=y
 CONFIG_MAGIC_SYSRQ=y
-# CONFIG_DEBUG_SPINLOCK is not set
-# CONFIG_CHECKING is not set
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_CHECKING=y
 # CONFIG_INIT_DEBUG is not set
-# CONFIG_IOMMU_DEBUG is not set
+CONFIG_IOMMU_DEBUG=y
 CONFIG_IOMMU_LEAK=y
 # CONFIG_DEBUG_STACKOVERFLOW is not set
+CONFIG_LOG_BUF_SHIFT=17
 
 #
 # Library routines
diff -urN linux-2.4.23-pre5/arch/x86_64/ia32/ia32_binfmt.c linux-2.4.23-pre6/arch/x86_64/ia32/ia32_binfmt.c
--- linux-2.4.23-pre5/arch/x86_64/ia32/ia32_binfmt.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/ia32/ia32_binfmt.c	2003-10-01 10:38:45.000000000 -0700
@@ -28,7 +28,6 @@
 
 #define ELF_NAME "elf/i386"
 
-#define IA32_PAGE_OFFSET ((current->personality & ADDR_LIMIT_3GB) ? 0xc0000000 : 0xFFFFe000)
 #define IA32_STACK_TOP IA32_PAGE_OFFSET
 #define ELF_ET_DYN_BASE		(IA32_PAGE_OFFSET/3 + 0x1000000)
 
diff -urN linux-2.4.23-pre5/arch/x86_64/ia32/ia32_ioctl.c linux-2.4.23-pre6/arch/x86_64/ia32/ia32_ioctl.c
--- linux-2.4.23-pre5/arch/x86_64/ia32/ia32_ioctl.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/ia32/ia32_ioctl.c	2003-10-01 10:38:45.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: ia32_ioctl.c,v 1.34 2003/05/08 06:34:01 ak Exp $
+/* $Id: ia32_ioctl.c,v 1.37 2003/08/20 11:00:23 ak Exp $
  * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
  *
  * Copyright (C) 1997-2000  Jakub Jelinek  (jakub@redhat.com)
@@ -2472,6 +2472,9 @@
 	mm_segment_t old_fs;
 	void *karg = &u;
 
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
 	switch (cmd) {
 	case VG_STATUS:
 		v = kmalloc(sizeof(vg_t), GFP_KERNEL);
@@ -3535,6 +3538,7 @@
 COMPATIBLE_IOCTL(TIOCGPTN)
 COMPATIBLE_IOCTL(TIOCSPTLCK)
 COMPATIBLE_IOCTL(TIOCSERGETLSR)
+COMPATIBLE_IOCTL(FIOQSIZE)
 /* Big F */
 COMPATIBLE_IOCTL(FBIOGET_VSCREENINFO)
 COMPATIBLE_IOCTL(FBIOPUT_VSCREENINFO)
@@ -4416,6 +4420,9 @@
 	struct ioctl_trans *t;
 	unsigned long hash = ioctl32_hash(cmd);
 
+	if (handler == NULL)
+		handler = (void *)sys_ioctl; 
+
 	lock_kernel(); 
 	for (t = (struct ioctl_trans *)ioctl32_hash_table[hash];
 	     t;
@@ -4555,7 +4562,7 @@
 			       "cmd(%08x){%s} arg(%08x) on %s\n",
 			       current->comm, current->pid,
 			       (int)fd, (unsigned int)cmd, buf, (unsigned int)arg,
-			       IS_ERR(fn) ? "???" : fn);
+			       fn);
 			if (path) 
 				free_page((unsigned long)path); 
 		}
diff -urN linux-2.4.23-pre5/arch/x86_64/ia32/ia32_signal.c linux-2.4.23-pre6/arch/x86_64/ia32/ia32_signal.c
--- linux-2.4.23-pre5/arch/x86_64/ia32/ia32_signal.c	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/ia32/ia32_signal.c	2003-10-01 10:38:45.000000000 -0700
@@ -7,7 +7,7 @@
  *  2000-06-20  Pentium III FXSR, SSE support by Gareth Hughes
  *  2000-12-*   x86-64 compatibility mode signal handling by Andi Kleen
  * 
- *  $Id: ia32_signal.c,v 1.31 2003/03/12 08:22:12 ak Exp $
+ *  $Id: ia32_signal.c,v 1.32 2003/09/06 18:10:44 ak Exp $
  */
 
 #include <linux/sched.h>
@@ -78,6 +78,10 @@
 		default:
 			err |= __put_user(from->si_uid, &to->si_uid);
 			break;
+		case __SI_POLL >> 16:
+			err |= __put_user(from->si_band, &to->si_band); 
+			err |= __put_user(from->si_fd, &to->si_fd); 
+			break;
 		/* case __SI_RT: This is not generated by the kernel as of now.  */
 		}
 		return err;
diff -urN linux-2.4.23-pre5/arch/x86_64/ia32/ia32entry.S linux-2.4.23-pre6/arch/x86_64/ia32/ia32entry.S
--- linux-2.4.23-pre5/arch/x86_64/ia32/ia32entry.S	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/ia32/ia32entry.S	2003-10-01 10:38:45.000000000 -0700
@@ -3,7 +3,7 @@
  * 		
  * Copyright 2000,2001 Andi Kleen, SuSE Labs.
  * 
- * $Id: ia32entry.S,v 1.40 2003/04/03 09:46:54 ak Exp $ 		
+ * $Id: ia32entry.S,v 1.41 2003/09/22 04:25:53 ak Exp $ 		
  */		 
 
 #include <asm/calling.h>
@@ -256,10 +256,10 @@
 	.quad sys32_adjtimex
 	.quad sys32_mprotect		/* 125 */
 	.quad sys32_sigprocmask
-	.quad sys32_module_warning	/* create_module */
-	.quad sys32_module_warning	/* init_module */
-	.quad sys32_module_warning	/* delete module */
-	.quad sys32_module_warning	/* 130  get_kernel_syms */
+	.quad sys32_create_module
+	.quad sys32_init_module
+	.quad sys32_delete_module
+	.quad sys32_get_kernel_syms 	/* 130 */
 	.quad ni_syscall	/* quotactl */ 
 	.quad sys_getpgid
 	.quad sys_fchdir
@@ -296,7 +296,7 @@
 	.quad sys_setresuid16
 	.quad sys_getresuid16	/* 165 */
 	.quad sys32_vm86_warning	/* vm86 */ 
-	.quad quiet_ni_syscall	/* query_module */
+	.quad sys32_query_module
 	.quad sys_poll
 	.quad sys32_nfsservctl
 	.quad sys_setresgid16	/* 170 */
diff -urN linux-2.4.23-pre5/arch/x86_64/ia32/ipc32.c linux-2.4.23-pre6/arch/x86_64/ia32/ipc32.c
--- linux-2.4.23-pre5/arch/x86_64/ia32/ipc32.c	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/ia32/ipc32.c	2003-10-01 10:38:45.000000000 -0700
@@ -16,6 +16,8 @@
 
 #include <asm/ia32.h>
 
+extern int sem_ctls[];
+
 /*
  * sys32_ipc() is the de-multiplexer for the SysV IPC calls in 32bit emulation..
  *
@@ -163,6 +165,7 @@
 #define SEMOP		 1
 #define SEMGET		 2
 #define SEMCTL		 3
+#define SEMTIMEDOP       4
 #define MSGSND		11
 #define MSGRCV		12
 #define MSGGET		13
@@ -638,7 +641,27 @@
 	switch (call) {
 	      case SEMOP:
 		/* struct sembuf is the same on 32 and 64bit :)) */
-		return sys_semop(first, (struct sembuf *)AA(ptr), second);
+		return sys_semtimedop(first, (struct sembuf *)AA(ptr), second, NULL);
+	      case SEMTIMEDOP: { 
+		int err;
+		mm_segment_t oldfs = get_fs();
+		struct timespec32 *ts32 = (struct timespec32 *)AA(fifth);
+		struct timespec ts;
+		if ((unsigned)second > sem_ctls[2])
+			return -EINVAL;		
+		if (ts32) { 
+			if (get_user(ts.tv_sec, &ts32->tv_sec) ||
+				__get_user(ts.tv_nsec, &ts32->tv_nsec) ||
+				verify_area(VERIFY_READ, (void *)AA(ptr), 
+								second*sizeof(struct sembuf)))
+					return -EFAULT; 
+		} 			
+		set_fs(KERNEL_DS); 
+	 	err = sys_semtimedop(first, (struct sembuf *)AA(ptr), second,
+					ts32 ? &ts : NULL); 
+		set_fs(oldfs);
+		return err; 			
+	      }	
 	      case SEMGET:
 		return sys_semget(first, second, third);
 	      case SEMCTL:
@@ -663,9 +686,7 @@
 	      case SHMCTL:
 		return shmctl32(first, second, (void *)AA(ptr));
 
-	      default:
-		return -EINVAL;
 	}
-	return -EINVAL;
+	return -ENOSYS;
 }
 
diff -urN linux-2.4.23-pre5/arch/x86_64/ia32/socket32.c linux-2.4.23-pre6/arch/x86_64/ia32/socket32.c
--- linux-2.4.23-pre5/arch/x86_64/ia32/socket32.c	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/ia32/socket32.c	2003-10-01 10:38:45.000000000 -0700
@@ -503,7 +503,6 @@
 		__u32 filter;
 	} *fprog32 = (struct sock_fprog32 *)optval;
 	struct sock_fprog kfprog;
-	unsigned int fsize;
 	mm_segment_t old_fs;
 	__u32 uptr;
 	int ret;
diff -urN linux-2.4.23-pre5/arch/x86_64/ia32/sys_ia32.c linux-2.4.23-pre6/arch/x86_64/ia32/sys_ia32.c
--- linux-2.4.23-pre5/arch/x86_64/ia32/sys_ia32.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/ia32/sys_ia32.c	2003-10-01 10:38:45.000000000 -0700
@@ -16,7 +16,7 @@
  *
  * This file assumes that there is a hole at the end of user address space.
  *
- * $Id: sys_ia32.c,v 1.58 2003/05/09 17:21:17 ak Exp $
+ * $Id: sys_ia32.c,v 1.62 2003/09/22 04:25:53 ak Exp $
  */
 
 #include <linux/config.h>
@@ -56,6 +56,7 @@
 #include <linux/rwsem.h>
 #include <linux/binfmts.h>
 #include <linux/init.h>
+#include <linux/highuid.h>
 #include <asm/mman.h>
 #include <asm/types.h>
 #include <asm/uaccess.h>
@@ -84,6 +85,10 @@
 #define low2highgid(gid) ((gid) == (u16)-1) ? (gid_t)-1 : (gid_t)(gid)
 extern int overflowuid,overflowgid; 
 
+typedef u16 old_uid_t;
+typedef u16 old_gid_t;
+
+#include "../../../kernel/uid16.c" 
 
 static int
 putstat(struct stat32 *ubuf, struct stat *kbuf)
@@ -93,8 +98,8 @@
 	    __put_user (kbuf->st_ino, &ubuf->st_ino) ||
 	    __put_user (kbuf->st_mode, &ubuf->st_mode) ||
 	    __put_user (kbuf->st_nlink, &ubuf->st_nlink) ||
-	    __put_user (kbuf->st_uid, &ubuf->st_uid) ||
-	    __put_user (kbuf->st_gid, &ubuf->st_gid) ||
+	    __put_user (high2lowuid(kbuf->st_uid), &ubuf->st_uid) ||
+	    __put_user (high2lowgid(kbuf->st_gid), &ubuf->st_gid) ||
 	    __put_user (kbuf->st_rdev, &ubuf->st_rdev) ||
 	    __put_user (kbuf->st_size, &ubuf->st_size) ||
 	    __put_user (kbuf->st_atime, &ubuf->st_atime) ||
@@ -475,7 +480,13 @@
 	    __put_user (kbuf->f_ffree, &ubuf->f_ffree) ||
 	    __put_user (kbuf->f_namelen, &ubuf->f_namelen) ||
 	    __put_user (kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) ||
-	    __put_user (kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]))
+	    __put_user (kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) ||
+	    __put_user (0, &ubuf->f_spare[0]) ||
+	    __put_user (0, &ubuf->f_spare[1]) ||
+	    __put_user (0, &ubuf->f_spare[2]) ||
+	    __put_user (0, &ubuf->f_spare[3]) ||
+	    __put_user (0, &ubuf->f_spare[4]) ||
+	    __put_user (0, &ubuf->f_spare[5]))
 		return -EFAULT;
 	return 0;
 }
@@ -925,11 +936,6 @@
 			    (struct timeval32 *)A(a.tvp));
 }
 
-struct timespec32 {
-	int 	tv_sec;
-	int	tv_nsec;
-};
-
 extern asmlinkage long sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp); 
 
 asmlinkage long
@@ -1517,10 +1523,30 @@
 	struct sysinfo s;
 	int ret;
 	mm_segment_t old_fs = get_fs ();
+	int bitcount = 0;
 	
 	set_fs (KERNEL_DS);
 	ret = sys_sysinfo(&s);
 	set_fs (old_fs);
+
+        /* Check to see if any memory value is too large for 32-bit and scale
+	 *  down if needed
+	 */
+	if ((s.totalram >> 32) || (s.totalswap >> 32)) {
+		while (s.mem_unit < PAGE_SIZE) {
+			s.mem_unit <<= 1;
+			bitcount++;
+		}
+		s.totalram >>= bitcount;
+		s.freeram >>= bitcount;
+		s.sharedram >>= bitcount;
+		s.bufferram >>= bitcount;
+		s.totalswap >>= bitcount;
+		s.freeswap >>= bitcount;
+		s.totalhigh >>= bitcount;
+		s.freehigh >>= bitcount;
+	}
+
 	if (verify_area(VERIFY_WRITE, info, sizeof(struct sysinfo32)) ||
 	    __put_user (s.uptime, &info->uptime) ||
 	    __put_user (s.loads[0], &info->loads[0]) ||
@@ -2534,17 +2560,422 @@
 }
 #endif
 
-long sys32_module_warning(void)
+#ifdef CONFIG_MODULES
+
+extern asmlinkage unsigned long sys_create_module(const char *name_user, size_t size);
+
+asmlinkage unsigned long sys32_create_module(const char *name_user, __kernel_size_t32 size)
 { 
-	static long warn_time = -(60*HZ); 
-	if (time_before(warn_time + 60*HZ,jiffies) && strcmp(current->comm,"klogd")) { 
-		printk(KERN_INFO "%s: 32bit modutils not supported on 64bit kernel\n",
-		       current->comm);
-		warn_time = jiffies;
+	return sys_create_module(name_user, (size_t)size);
+}
+
+extern asmlinkage int sys_init_module(const char *name_user, struct module *mod_user);
+
+/* Hey, when you're trying to init module, take time and prepare us a nice 64bit
+ * module structure, even if from 32bit modutils... Why to pollute kernel... :))
+ */
+asmlinkage int sys32_init_module(const char *name_user, struct module *mod_user)
+{
+	return sys_init_module(name_user, mod_user);
+}
+
+extern asmlinkage int sys_delete_module(const char *name_user);
+
+asmlinkage int sys32_delete_module(const char *name_user)
+{
+	return sys_delete_module(name_user);
+}
+
+struct module_info32 {
+	u32 addr;
+	u32 size;
+	u32 flags;
+	s32 usecount;
+};
+
+/* Query various bits about modules.  */
+
+static inline long
+get_mod_name(const char *user_name, char **buf)
+{
+	unsigned long page;
+	long retval;
+
+	if ((unsigned long)user_name >= TASK_SIZE
+	    && !segment_eq(get_fs (), KERNEL_DS))
+		return -EFAULT;
+
+	page = __get_free_page(GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+
+	retval = strncpy_from_user((char *)page, user_name, PAGE_SIZE);
+	if (retval > 0) {
+		if (retval < PAGE_SIZE) {
+			*buf = (char *)page;
+			return retval;
 	} 
-	return -ENOSYS ;
+		retval = -ENAMETOOLONG;
+	} else if (!retval)
+		retval = -EINVAL;
+
+	free_page(page);
+	return retval;
+}
+
+static inline void
+put_mod_name(char *buf)
+{
+	free_page((unsigned long)buf);
 } 
 
+static __inline__ struct module *find_module(const char *name)
+{
+	struct module *mod;
+
+	for (mod = module_list; mod ; mod = mod->next) {
+		if (mod->flags & MOD_DELETED)
+			continue;
+		if (!strcmp(mod->name, name))
+			break;
+	}
+
+	return mod;
+}
+
+static int
+qm_modules(char *buf, size_t bufsize, __kernel_size_t32 *ret)
+{
+	struct module *mod;
+	size_t nmod, space, len;
+
+	nmod = space = 0;
+
+	for (mod = module_list; mod->next != NULL; mod = mod->next, ++nmod) {
+		len = strlen(mod->name)+1;
+		if (len > bufsize)
+			goto calc_space_needed;
+		if (copy_to_user(buf, mod->name, len))
+			return -EFAULT;
+		buf += len;
+		bufsize -= len;
+		space += len;
+	}
+
+	if (put_user(nmod, ret))
+		return -EFAULT;
+	else
+		return 0;
+
+calc_space_needed:
+	space += len;
+	while ((mod = mod->next)->next != NULL)
+		space += strlen(mod->name)+1;
+
+	if (put_user(space, ret))
+		return -EFAULT;
+	else
+		return -ENOSPC;
+}
+
+static int
+qm_deps(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret)
+{
+	size_t i, space, len;
+
+	if (mod->next == NULL)
+		return -EINVAL;
+	if (!MOD_CAN_QUERY(mod))
+		return put_user(0, ret);
+
+	space = 0;
+	for (i = 0; i < mod->ndeps; ++i) {
+		const char *dep_name = mod->deps[i].dep->name;
+
+		len = strlen(dep_name)+1;
+		if (len > bufsize)
+			goto calc_space_needed;
+		if (copy_to_user(buf, dep_name, len))
+			return -EFAULT;
+		buf += len;
+		bufsize -= len;
+		space += len;
+	}
+
+	return put_user(i, ret);
+
+calc_space_needed:
+	space += len;
+	while (++i < mod->ndeps)
+		space += strlen(mod->deps[i].dep->name)+1;
+
+	if (put_user(space, ret))
+		return -EFAULT;
+	else
+		return -ENOSPC;
+}
+
+static int
+qm_refs(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret)
+{
+	size_t nrefs, space, len;
+	struct module_ref *ref;
+
+	if (mod->next == NULL)
+		return -EINVAL;
+	if (!MOD_CAN_QUERY(mod))
+		if (put_user(0, ret))
+			return -EFAULT;
+		else
+			return 0;
+
+	space = 0;
+	for (nrefs = 0, ref = mod->refs; ref ; ++nrefs, ref = ref->next_ref) {
+		const char *ref_name = ref->ref->name;
+
+		len = strlen(ref_name)+1;
+		if (len > bufsize)
+			goto calc_space_needed;
+		if (copy_to_user(buf, ref_name, len))
+			return -EFAULT;
+		buf += len;
+		bufsize -= len;
+		space += len;
+	}
+
+	if (put_user(nrefs, ret))
+		return -EFAULT;
+	else
+		return 0;
+
+calc_space_needed:
+	space += len;
+	while ((ref = ref->next_ref) != NULL)
+		space += strlen(ref->ref->name)+1;
+
+	if (put_user(space, ret))
+		return -EFAULT;
+	else
+		return -ENOSPC;
+}
+
+static inline int
+qm_symbols(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret)
+{
+	size_t i, space, len;
+	struct module_symbol *s;
+	char *strings;
+	unsigned *vals;
+
+	if (!MOD_CAN_QUERY(mod))
+		if (put_user(0, ret))
+			return -EFAULT;
+		else
+			return 0;
+
+	space = mod->nsyms * 2*sizeof(u32);
+
+	i = len = 0;
+	s = mod->syms;
+
+	if (space > bufsize)
+		goto calc_space_needed;
+
+	if (!access_ok(VERIFY_WRITE, buf, space))
+		return -EFAULT;
+
+	bufsize -= space;
+	vals = (unsigned *)buf;
+	strings = buf+space;
+
+	for (; i < mod->nsyms ; ++i, ++s, vals += 2) {
+		len = strlen(s->name)+1;
+		if (len > bufsize)
+			goto calc_space_needed;
+
+		if (copy_to_user(strings, s->name, len)
+		    || __put_user(s->value, vals+0)
+		    || __put_user(space, vals+1))
+			return -EFAULT;
+
+		strings += len;
+		bufsize -= len;
+		space += len;
+	}
+
+	if (put_user(i, ret))
+		return -EFAULT;
+	else
+		return 0;
+
+calc_space_needed:
+	for (; i < mod->nsyms; ++i, ++s)
+		space += strlen(s->name)+1;
+
+	if (put_user(space, ret))
+		return -EFAULT;
+	else
+		return -ENOSPC;
+}
+
+static inline int
+qm_info(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret)
+{
+	int error = 0;
+
+	if (mod->next == NULL)
+		return -EINVAL;
+
+	if (sizeof(struct module_info32) <= bufsize) {
+		struct module_info32 info;
+		info.addr = (unsigned long)mod;
+		info.size = mod->size;
+		info.flags = mod->flags;
+		info.usecount =
+			((mod_member_present(mod, can_unload)
+			  && mod->can_unload)
+			 ? -1 : atomic_read(&mod->uc.usecount));
+
+		if (copy_to_user(buf, &info, sizeof(struct module_info32)))
+			return -EFAULT;
+	} else
+		error = -ENOSPC;
+
+	if (put_user(sizeof(struct module_info32), ret))
+		return -EFAULT;
+
+	return error;
+}
+
+asmlinkage int sys32_query_module(char *name_user, int which, char *buf, __kernel_size_t32 bufsize, u32 ret)
+{
+	struct module *mod;
+	int err;
+
+	lock_kernel();
+	if (name_user == 0) {
+		/* This finds "kernel_module" which is not exported. */
+		for(mod = module_list; mod->next != NULL; mod = mod->next)
+			;
+	} else {
+		long namelen;
+		char *name;
+
+		if ((namelen = get_mod_name(name_user, &name)) < 0) {
+			err = namelen;
+			goto out;
+		}
+		err = -ENOENT;
+		if (namelen == 0) {
+			/* This finds "kernel_module" which is not exported. */
+			for(mod = module_list; mod->next != NULL; mod = mod->next)
+				;
+		} else if ((mod = find_module(name)) == NULL) {
+			put_mod_name(name);
+			goto out;
+		}
+		put_mod_name(name);
+	}
+
+	switch (which)
+	{
+	case 0:
+		err = 0;
+		break;
+	case QM_MODULES:
+		err = qm_modules(buf, bufsize, (__kernel_size_t32 *)AA(ret));
+		break;
+	case QM_DEPS:
+		err = qm_deps(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret));
+		break;
+	case QM_REFS:
+		err = qm_refs(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret));
+		break;
+	case QM_SYMBOLS:
+		err = qm_symbols(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret));
+		break;
+	case QM_INFO:
+		err = qm_info(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret));
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+out:
+	unlock_kernel();
+	return err;
+}
+
+struct kernel_sym32 {
+	u32 value;
+	char name[60];
+};
+		 
+extern asmlinkage int sys_get_kernel_syms(struct kernel_sym *table);
+
+asmlinkage int sys32_get_kernel_syms(struct kernel_sym32 *table)
+{
+	int len, i;
+	struct kernel_sym *tbl;
+	mm_segment_t old_fs;
+	
+	len = sys_get_kernel_syms(NULL);
+	if (!table) return len;
+	tbl = kmalloc (len * sizeof (struct kernel_sym), GFP_KERNEL);
+	if (!tbl) return -ENOMEM;
+	old_fs = get_fs();
+	set_fs (KERNEL_DS);
+	sys_get_kernel_syms(tbl);
+	set_fs (old_fs);
+	for (i = 0; i < len; i++, table++) {
+		if (put_user (tbl[i].value, &table->value) ||
+		    copy_to_user (table->name, tbl[i].name, 60))
+			break;
+	}
+	kfree (tbl);
+	return i;
+}
+
+#else /* CONFIG_MODULES */
+
+asmlinkage unsigned long
+sys32_create_module(const char *name_user, size_t size)
+{
+	return -ENOSYS;
+}
+
+asmlinkage int
+sys32_init_module(const char *name_user, struct module *mod_user)
+{
+	return -ENOSYS;
+}
+
+asmlinkage int
+sys32_delete_module(const char *name_user)
+{
+	return -ENOSYS;
+}
+
+asmlinkage int
+sys32_query_module(const char *name_user, int which, char *buf, size_t bufsize,
+		 size_t *ret)
+{
+	/* Let the program know about the new interface.  Not that
+	   it'll do them much good.  */
+	if (which == 0)
+		return 0;
+
+	return -ENOSYS;
+}
+
+asmlinkage int
+sys32_get_kernel_syms(struct kernel_sym *table)
+{
+	return -ENOSYS;
+}
+
+#endif  /* CONFIG_MODULES */
+
 long sys32_vm86_warning(void)
 { 
 	static long warn_time = -(60*HZ); 
@@ -2565,7 +2996,7 @@
 
 static int __init ia32_init (void)
 {
-	printk("IA32 emulation $Id: sys_ia32.c,v 1.58 2003/05/09 17:21:17 ak Exp $\n");  
+	printk("IA32 emulation $Id: sys_ia32.c,v 1.62 2003/09/22 04:25:53 ak Exp $\n");  
 	ia32_exec_domain.signal_map = default_exec_domain.signal_map;
 	ia32_exec_domain.signal_invmap = default_exec_domain.signal_invmap;
 	register_exec_domain(&ia32_exec_domain);
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/acpi.c linux-2.4.23-pre6/arch/x86_64/kernel/acpi.c
--- linux-2.4.23-pre5/arch/x86_64/kernel/acpi.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/acpi.c	2003-10-01 10:38:45.000000000 -0700
@@ -44,6 +44,7 @@
 #include <asm/io_apic.h>
 #include <asm/proto.h>
 
+extern int acpi_disabled;
 
 #define PREFIX			"ACPI: "
 
@@ -352,6 +353,28 @@
 #ifdef CONFIG_X86_LOCAL_APIC
 
 	/* 
+	 * ACPI interpreter is required to complete interrupt setup,
+	 * so if it is off, don't enumerate the io-apics with ACPI.
+	 * If MPS is present, it will handle them,
+	 * otherwise the system will stay in PIC mode
+	 */
+	if (acpi_disabled) {
+		return 1;
+	}
+
+	if (!use_acpi_pci)
+		return 0; 
+
+	/* If "nolocalapic" is specified don't look further */
+	extern int apic_disabled;
+	if (apic_disabled) {
+		printk(KERN_INFO PREFIX "Skipping Local/IO-APIC probe due to \"nolocalapic\"\n");
+		return 0;	
+	}	
+	printk(KERN_INFO PREFIX "Parsing Local APIC info in MADT\n"); 
+	
+
+	/* 
 	 * MADT
 	 * ----
 	 * Parse the Multiple APIC Description Table (MADT), if exists.
@@ -412,6 +435,15 @@
 #ifdef CONFIG_X86_IO_APIC
 
 	/* 
+	 * if "noapic" boot option, don't look for IO-APICs
+	 */
+	if (ioapic_setup_disabled()) {
+		printk(KERN_INFO PREFIX "Skipping IOAPIC probe "
+			"due to 'noapic' option.\n");
+		return 1;
+        }
+
+	/* 
 	 * I/O APIC 
 	 * --------
 	 */
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/acpi_wakeup.S linux-2.4.23-pre6/arch/x86_64/kernel/acpi_wakeup.S
--- linux-2.4.23-pre5/arch/x86_64/kernel/acpi_wakeup.S	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/acpi_wakeup.S	2003-10-01 10:38:45.000000000 -0700
@@ -209,7 +209,7 @@
 
 	movw	$0x0e00 + '!', %ds:(0xb801a)
 	movq	saved_eip, %rax
-	jmp	%rax
+	jmp	*%rax
 
 .code32
 
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/bluesmoke.c linux-2.4.23-pre6/arch/x86_64/kernel/bluesmoke.c
--- linux-2.4.23-pre5/arch/x86_64/kernel/bluesmoke.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/bluesmoke.c	2003-10-01 10:38:45.000000000 -0700
@@ -186,7 +186,7 @@
 	[0] = "err cpu1",
 };
 
-static void check_k8_nb(void)
+static void check_k8_nb(int header)
 {
 	struct pci_dev *nb;
 	nb = find_k8_nb(); 
@@ -198,6 +198,9 @@
 	pci_read_config_dword(nb, 0x4c, &statushigh);
 	if (!(statushigh & (1<<31)))
 		return;
+	if (header) 
+		printk(KERN_ERR "CPU %d: Silent Northbridge MCE\n", smp_processor_id());
+
 	printk(KERN_ERR "Northbridge status %08x%08x\n",
 	       statushigh,statuslow); 
 
@@ -257,9 +260,11 @@
 	rdmsrl(MSR_IA32_MCG_STATUS, status); 
 	if ((status & (1<<2)) == 0) { 
 		if (!regs) 
-			check_k8_nb();
+			check_k8_nb(1);
 		return; 
 	}
+	printk(KERN_EMERG "CPU %d: Machine Check Exception: %016Lx\n", smp_processor_id(), status);
+
 	if (status & 1)
 		printk(KERN_EMERG "MCG_STATUS: unrecoverable\n"); 
 
@@ -277,7 +282,7 @@
 	if (nbstatus & (1UL<57))
 		printk(KERN_EMERG "Unrecoverable condition\n"); 
 		
-	check_k8_nb();
+	check_k8_nb(0);
 
 	if (nbstatus & (1UL<<58)) { 
 		u64 adr;
@@ -338,7 +343,6 @@
 {
 	u64 cap;
 	int i;
-	struct pci_dev *nb; 
 
 	if (!test_bit(X86_FEATURE_MCE, &c->x86_capability) || 
 	    !test_bit(X86_FEATURE_MCA, &c->x86_capability))
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/e820.c linux-2.4.23-pre6/arch/x86_64/kernel/e820.c
--- linux-2.4.23-pre5/arch/x86_64/kernel/e820.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/e820.c	2003-10-01 10:38:45.000000000 -0700
@@ -1,7 +1,7 @@
 /* 
  * Handle the memory map.
  * The functions here do the job until bootmem takes over.
- * $Id: e820.c,v 1.6 2002/10/15 09:35:16 ak Exp $
+ * $Id: e820.c,v 1.10 2003/09/25 03:22:05 ak Exp $
  */
 #include <linux/config.h>
 #include <linux/kernel.h>
@@ -12,6 +12,7 @@
 #include <asm/page.h>
 #include <asm/e820.h>
 #include <asm/proto.h>
+#include <asm/acpi.h>
 #include <asm/bootsetup.h>
 
 extern unsigned long table_start, table_end;
@@ -488,7 +489,6 @@
 			mem_size = ALT_MEM_K;
 			who = "BIOS-e801";
 		}
-
 		e820.nr_map = 0;
 		add_memory_region(0, LOWMEMSIZE(), E820_RAM);
 		add_memory_region(HIGH_MEMORY, mem_size << 10, E820_RAM);
@@ -500,7 +500,6 @@
 extern char command_line[], saved_command_line[];
 extern int fallback_aper_order;
 extern int iommu_setup(char *opt);
-extern int acpi_disabled;
 
 void __init parse_mem_cmdline (char ** cmdline_p)
 {
@@ -541,6 +540,22 @@
 #endif
  		else if (!memcmp(from, "acpi=off", 8))
   			acpi_disabled = 1;
+		else if (!memcmp(from,"maxcpus=0",9)) {
+			disable_ioapic_setup();
+			apic_disabled = 1;
+		}
+		
+		else if (!memcmp(from, "noapic", 6)) 
+			disable_ioapic_setup();
+		else if (!memcmp(from, "nolocalapic", 11) || !memcmp(from,"nolapic",7))
+			apic_disabled = 1;
+		else if (!memcmp(from, "pci=noacpi", 10))
+			use_acpi_pci = 0;
+		else if (!memcmp(from,"apic",4)) {
+			extern int ioapic_force;
+			ioapic_force = 1;
+			skip_ioapic_setup = 0;
+		}
 		
 	next:
 		c = *(from++);
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/head.S linux-2.4.23-pre6/arch/x86_64/kernel/head.S
--- linux-2.4.23-pre5/arch/x86_64/kernel/head.S	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/head.S	2003-10-01 10:38:45.000000000 -0700
@@ -6,7 +6,7 @@
  *  Copyright (C) 2000 Karsten Keil <kkeil@suse.de>
  *  Copyright (C) 2001,2002 Andi Kleen <ak@suse.de>
  *
- *  $Id: head.S,v 1.56 2003/05/12 14:38:43 ak Exp $
+ *  $Id: head.S,v 1.57 2003/07/11 15:58:46 ak Exp $
  */
 
 
@@ -312,7 +312,8 @@
 ENTRY(level3_physmem_pgt)
 	.quad	0x0000000000105007		/* -> level2_kernel_pgt (so that __va works even before pagetable_init) */
 
-.org 0xb000
+	.org 0xb000
+#ifdef CONFIG_ACPI_SLEEP
 ENTRY(wakeup_level4_pgt)
         .quad   0x0000000000102007              /* -> level3_ident_pgt */
         .fill   255,8,0
@@ -320,6 +321,7 @@
         .fill   254,8,0
         /* (2^48-(2*1024*1024*1024))/(2^39) = 511 */
         .quad   0x0000000000103007              /* -> level3_kernel_pgt */
+#endif
 
 .data
 
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/io_apic.c linux-2.4.23-pre6/arch/x86_64/kernel/io_apic.c
--- linux-2.4.23-pre5/arch/x86_64/kernel/io_apic.c	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/io_apic.c	2003-10-01 10:38:45.000000000 -0700
@@ -165,6 +165,14 @@
 	struct IO_APIC_route_entry entry;
 	unsigned long flags;
 
+        /* Check delivery_mode to be sure we're not clearing an SMI pin */
+        spin_lock_irqsave(&ioapic_lock, flags);
+        *(((int*)&entry) + 0) = io_apic_read(apic, 0x10 + 2 * pin);
+        *(((int*)&entry) + 1) = io_apic_read(apic, 0x11 + 2 * pin);
+        spin_unlock_irqrestore(&ioapic_lock, flags);
+        if (entry.delivery_mode == dest_SMI)
+                return;
+
 	/*
 	 * Disable it in the IO-APIC irq-routing table:
 	 */
@@ -194,9 +202,11 @@
 int pirq_entries [MAX_PIRQS];
 int pirqs_enabled;
 int skip_ioapic_setup;
+int ioapic_force; 
 
 static int __init noioapic_setup(char *str)
 {
+	ioapic_force = 1;
 	skip_ioapic_setup = 1;
 	return 1;
 }
@@ -205,12 +215,66 @@
 
 static int __init ioapic_setup(char *str)
 {
+	ioapic_force = 1;
 	skip_ioapic_setup = 0;
 	return 1;
 }
 
 __setup("apic", ioapic_setup);
 
+#ifndef CONFIG_SMP
+#include <asm/pci-direct.h>
+#include <linux/pci_ids.h>
+#include <linux/pci.h>
+
+/* Temporary Hack. Nvidia and VIA boards currently only work with IO-APIC
+   off. Check for an Nvidia or VIA PCI bridge and turn it off.
+   Use pci direct infrastructure because this runs before the PCI subsystem. 
+
+   Can be overwritten with "apic" */
+void __init check_ioapic(void) 
+{ 
+	int num,slot,func; 
+	if (ioapic_force) 
+		return; 
+
+	/* Poor man's PCI discovery */
+	for (num = 0; num < 32; num++) { 
+		for (slot = 0; slot < 32; slot++) { 
+			for (func = 0; func < 8; func++) { 
+				u32 class;
+				u32 vendor;
+				class = read_pci_config(num,slot,func,
+							PCI_CLASS_REVISION);
+				if (class == 0xffffffff)
+					break; 
+
+		       		if ((class >> 16) != PCI_CLASS_BRIDGE_PCI)
+					continue; 
+
+				vendor = read_pci_config(num, slot, func, 
+							 PCI_VENDOR_ID);
+				vendor &= 0xffff;
+				switch (vendor) { 
+				case PCI_VENDOR_ID_NVIDIA: 
+				case PCI_VENDOR_ID_VIA:
+					printk(KERN_INFO 
+     "PCI bridge %02x:%02x from %x found. Setting \"noapic\". Overwrite with \"apic\"\n",
+					       num,slot,vendor); 
+					skip_ioapic_setup = 1;
+					return;
+				} 
+
+				/* No multi-function device? */
+				u8 type = read_pci_config_byte(num,slot,func,
+							       PCI_HEADER_TYPE);
+				if (!(type & 0x80))
+					break;
+			} 
+		}
+	}
+} 
+#endif
 
 static int __init ioapic_pirq_setup(char *str)
 {
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/ioport.c linux-2.4.23-pre6/arch/x86_64/kernel/ioport.c
--- linux-2.4.23-pre5/arch/x86_64/kernel/ioport.c	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/ioport.c	2003-10-01 10:38:45.000000000 -0700
@@ -19,35 +19,13 @@
 /* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */
 static void set_bitmap(unsigned long *bitmap, short base, short extent, int new_value)
 {
-	unsigned long mask;
-	unsigned long *bitmap_base = bitmap + base / sizeof(long);
-	unsigned low_index = base & (BITS_PER_LONG - 1);
-	int length = low_index + extent;
-
-	if (low_index != 0) {
-		mask = (~0UL << low_index);
-		if (length < 64)
-			mask &= ~(~0UL << length);
+	int i;
 		if (new_value)
-			*bitmap_base++ |= mask;
+		for (i = base; i < base + extent; i++) 
+			__set_bit(i, bitmap); 
 		else
-			*bitmap_base++ &= ~mask;
-		length -= 64;
-	}
-
-	mask = (new_value ? ~0UL : 0UL);
-	while (length >= 64) {
-		*bitmap_base++ = mask;
-		length -= 64;
-	}
-
-	if (length > 0) {
-		mask = ~(~0UL << length);
-		if (new_value)
-			*bitmap_base++ |= mask;
-		else
-			*bitmap_base++ &= ~mask;
-	}
+		for (i = base; i < base + extent; i++) 
+			clear_bit(i, bitmap); 
 }
 
 /*
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/mpparse.c linux-2.4.23-pre6/arch/x86_64/kernel/mpparse.c
--- linux-2.4.23-pre5/arch/x86_64/kernel/mpparse.c	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/mpparse.c	2003-10-01 10:38:45.000000000 -0700
@@ -139,9 +139,12 @@
 	str[6] = 0;
 	Dprintk("Bus #%d is %s\n", m->mpc_busid, str);
 
-	if (m->mpc_busid >= MAX_MP_BUSSES) {
+#if MAX_MP_BUSSES < 256
+	if ((long)m->mpc_busid >= MAX_MP_BUSSES) {
 		printk(KERN_ERR "MAX_MP_BUSSES ERROR mpc_busid %d, max %d\n", m->mpc_busid, MAX_MP_BUSSES);
-	} else if (strncmp(str, "ISA", 3) == 0) {
+	} else 
+#endif	
+	if (strncmp(str, "ISA", 3) == 0) {
 		mp_bus_id_to_type[m->mpc_busid] = MP_BUS_ISA;
 	} else if (strncmp(str, "EISA", 4) == 0) {
 		mp_bus_id_to_type[m->mpc_busid] = MP_BUS_EISA;
@@ -756,7 +759,7 @@
 	mp_ioapic_routing[idx].irq_end = irq_base + 
 		io_apic_get_redir_entries(idx);
 
-	printk("IOAPIC[%d]: apic_id %d, version %d, address 0x%lx, "
+	printk("IOAPIC[%d]: apic_id %d, version %d, address 0x%x, "
 		"IRQ %d-%d\n", idx, mp_ioapics[idx].mpc_apicid, 
 		mp_ioapics[idx].mpc_apicver, mp_ioapics[idx].mpc_apicaddr,
 		mp_ioapic_routing[idx].irq_start,
@@ -883,8 +886,6 @@
 
 #ifndef CONFIG_ACPI_HT_ONLY
 
-/* Ensure the ACPI SCI interrupt level is active low, edge-triggered */
-
 extern FADT_DESCRIPTOR acpi_fadt;
 
 void __init mp_config_ioapic_for_sci(int irq)
@@ -893,6 +894,7 @@
 	int ioapic_pin;
 	struct acpi_table_madt* madt;
 	struct acpi_table_int_src_ovr *entry = NULL;
+	acpi_interrupt_flags flags;
 	void *madt_end;
 	acpi_status status;
 
@@ -911,23 +913,42 @@
 
 		while ((void *) entry < madt_end) {
                 	if (entry->header.type == ACPI_MADT_INT_SRC_OVR &&
-			    acpi_fadt.sci_int == entry->global_irq)
-                		return;
+ 			    acpi_fadt.sci_int == entry->bus_irq)
+ 				goto found;
 
                 	entry = (struct acpi_table_int_src_ovr *)
                 	        ((unsigned long) entry + entry->header.length);
         	}
 	}
+ 	/*
+ 	 * Although the ACPI spec says that the SCI should be level/low
+ 	 * don't reprogram it unless there is an explicit MADT OVR entry
+ 	 * instructing us to do so -- otherwise we break Tyan boards which
+ 	 * have the SCI wired edge/high but no MADT OVR.
+ 	 */
+ 	return;
+
+found:
+ 	/*
+ 	 * See the note at the end of ACPI 2.0b section
+ 	 * 5.2.10.8 for what this is about.
+ 	 */
+ 	flags = entry->flags;
+ 	acpi_fadt.sci_int = entry->global_irq;
+ 	irq = entry->global_irq;
 
 	ioapic = mp_find_ioapic(irq);
 
 	ioapic_pin = irq - mp_ioapic_routing[ioapic].irq_start;
 
-	io_apic_set_pci_routing(ioapic, ioapic_pin, irq, 1, 1);
+ 	io_apic_set_pci_routing(ioapic, ioapic_pin, irq, 
+ 				(flags.trigger >> 1) , (flags.polarity >> 1));
 }
 
 #endif /*CONFIG_ACPI_HT_ONLY*/
 
+int acpi_tolerant;
+
 #ifdef CONFIG_ACPI_PCI
 
 void __init mp_parse_prt (void)
@@ -961,6 +982,13 @@
 		}
 
 		irq = entry->link.index;
+
+  		/* Don't set up the ACPI SCI because it's already set up */
+                if (acpi_fadt.sci_int == irq) {
+                         entry->irq = irq; /*we still need to set entry's irq*/
+ 			continue;
+                }
+
 		ioapic = mp_find_ioapic(irq);
 		if (ioapic < 0)
 			continue;
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/nmi.c linux-2.4.23-pre6/arch/x86_64/kernel/nmi.c
--- linux-2.4.23-pre5/arch/x86_64/kernel/nmi.c	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/nmi.c	2003-10-01 10:38:45.000000000 -0700
@@ -30,6 +30,7 @@
 unsigned int nmi_watchdog = NMI_LOCAL_APIC;
 static unsigned int nmi_hz = HZ;
 unsigned int nmi_perfctr_msr;	/* the MSR to reset in NMI handler */
+int panic_on_timeout;
 
 int nmi_watchdog_disabled; 
 
@@ -121,6 +122,13 @@
 {
 	int nmi;
 
+	if (!strncmp(str,"panic",5)) {
+		panic_on_timeout = 1;
+		str = strchr(str, ',');
+		if (!str) 
+			return 1; 
+		++str;	
+	}			
 	get_option(&str, &nmi);
 
 	if (nmi >= NMI_INVALID)
@@ -380,6 +388,8 @@
 			bust_spinlocks(1);
 			printk("NMI Watchdog detected LOCKUP on CPU%d, eip %16lx, registers:\n", cpu, regs->rip);
 			show_registers(regs);
+			if (panic_on_timeout)
+				panic("NMI lockup");
 			printk("console shuts up ...\n");
 			console_silent();
 			spin_unlock(&nmi_print_lock);
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/pci-gart.c linux-2.4.23-pre6/arch/x86_64/kernel/pci-gart.c
--- linux-2.4.23-pre5/arch/x86_64/kernel/pci-gart.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/pci-gart.c	2003-10-01 10:38:45.000000000 -0700
@@ -8,7 +8,7 @@
  * See Documentation/DMA-mapping.txt for the interface specification.
  * 
  * Copyright 2002 Andi Kleen, SuSE Labs.
- * $Id: pci-gart.c,v 1.27 2003/08/05 18:25:31 ak Exp $
+ * $Id: pci-gart.c,v 1.28 2003/09/19 07:01:58 ak Exp $
  */
 
 #include <linux/config.h>
@@ -588,9 +588,12 @@
 	    if (!memcmp(p, "memaper", 7)) { 
 		    fallback_aper_force = 1; 
 		    p += 7; 
-		    if (*p == '=' && get_option(&p, &arg))
+		    if (*p == '=') { 
+			    ++p;
+			    if (get_option(&p, &arg))
 			    fallback_aper_order = arg;
 	    } 
+	    } 
 #ifdef CONFIG_IOMMU_LEAK
 	    if (!memcmp(p,"leak", 4)) { 
 		    leak_trace = 1;
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/pci-pc.c linux-2.4.23-pre6/arch/x86_64/kernel/pci-pc.c
--- linux-2.4.23-pre5/arch/x86_64/kernel/pci-pc.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/pci-pc.c	2003-10-01 10:38:45.000000000 -0700
@@ -23,9 +23,11 @@
 #include <asm/segment.h>
 #include <asm/io.h>
 #include <asm/mpspec.h>
+#include <asm/proto.h>
 
 #include "pci-x86_64.h"
 
+int use_acpi_pci = 1;
 unsigned int pci_probe = PCI_PROBE_CONF1 | PCI_PROBE_CONF2;
 
 int pcibios_last_bus = -1;
@@ -588,7 +590,7 @@
 
 	printk(KERN_INFO "PCI: Probing PCI hardware\n");
 #ifdef CONFIG_ACPI_PCI
- 	if (!acpi_pci_irq_init())
+ 	if (!acpi_disabled && use_acpi_pci && !acpi_pci_irq_init())
  		pci_using_acpi_prt = 1;
 #endif
  	if (!pci_using_acpi_prt) {
@@ -661,7 +663,7 @@
 		return err;
 
 #ifdef CONFIG_ACPI_PCI
-	if (pci_using_acpi_prt) {
+	if (use_acpi_pci && pci_using_acpi_prt) {
 		acpi_pci_irq_enable(dev);
 		return 0;
 	}
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/pci-x86_64.c linux-2.4.23-pre6/arch/x86_64/kernel/pci-x86_64.c
--- linux-2.4.23-pre5/arch/x86_64/kernel/pci-x86_64.c	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/pci-x86_64.c	2003-10-01 10:38:45.000000000 -0700
@@ -294,6 +294,17 @@
 	}
 }
 
+void __init pcibios_set_cacheline_size(void)
+{
+	struct cpuinfo_x86 *c = &boot_cpu_data;
+
+	pci_cache_line_size = 32 >> 2;
+	if (c->x86 >= 6 && c->x86_vendor == X86_VENDOR_AMD)
+		pci_cache_line_size = 64 >> 2;	/* K7 & K8 */
+	else if (c->x86 > 6 && c->x86_vendor == X86_VENDOR_INTEL)
+		pci_cache_line_size = 128 >> 2;	/* P4 */
+}
+
 void __init pcibios_resource_survey(void)
 {
 	DBG("PCI: Allocating resources\n");
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/pci-x86_64.h linux-2.4.23-pre6/arch/x86_64/kernel/pci-x86_64.h
--- linux-2.4.23-pre5/arch/x86_64/kernel/pci-x86_64.h	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/pci-x86_64.h	2003-10-01 10:38:45.000000000 -0700
@@ -27,8 +27,10 @@
 /* pci-i386.c */
 
 extern unsigned int pcibios_max_latency;
+extern u8 pci_cache_line_size;
 
 void pcibios_resource_survey(void);
+void pcibios_set_cacheline_size(void);
 int pcibios_enable_resources(struct pci_dev *, int mask);
 
 /* pci-pc.c */
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/process.c linux-2.4.23-pre6/arch/x86_64/kernel/process.c
--- linux-2.4.23-pre5/arch/x86_64/kernel/process.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/process.c	2003-10-01 10:38:45.000000000 -0700
@@ -9,7 +9,7 @@
  *  X86-64 port
  *	Andi Kleen.
  * 
- *  $Id: process.c,v 1.70 2003/06/09 05:18:21 ak Exp $
+ *  $Id: process.c,v 1.74 2003/08/13 13:43:16 ak Exp $
  */
 
 /*
@@ -168,7 +168,7 @@
 static int reboot_mode = 0; 
 
 /* reboot=b[ios] | t[riple] | k[bd] [, [w]arm | [c]old]
-   bios	  Use the CPU reboto vector for warm reset
+   bios	  Use the CPU reboot vector for warm reset
    warm   Don't set the cold reboot flag
    cold   Set the cold reboto flag
    triple Force a triple fault (init)
@@ -227,7 +227,7 @@
 		      [target] "b" (WARMBOOT_TRAMP));
 }
 
-static inline void kb_wait(void)
+static void kb_wait(void)
 {
 	int i;
 
@@ -252,6 +252,7 @@
 
 	/* AP calling this. Just halt */
 	if (cpuid != boot_cpu_id) { 
+		printk("CPU %d SMP halt\n", cpuid); 
 		for (;;)
 			asm("hlt");
 	}
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/setup.c linux-2.4.23-pre6/arch/x86_64/kernel/setup.c
--- linux-2.4.23-pre5/arch/x86_64/kernel/setup.c	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/setup.c	2003-10-01 10:38:45.000000000 -0700
@@ -295,6 +295,11 @@
 #endif
 
 	paging_init();
+#if !defined(CONFIG_SMP) && defined(CONFIG_X86_IO_APIC)
+	extern void check_ioapic(void);
+	check_ioapic();
+#endif
+
 #ifdef CONFIG_ACPI_BOOT
 	/*
 	 * Parse the ACPI tables for possible boot-time SMP configuration.
@@ -377,7 +382,10 @@
 		c->x86_cache_size=(ecx>>24)+(edx>>24);	
 		if (n >= 0x80000006) {
 			printk(KERN_INFO "CPU: L2 Cache: %dK (%d bytes/line/%d way)\n",
-			       ecx_2>>16, ecx_2&0xFF, (ecx_2>>12)&0xf);
+			       ecx_2>>16, ecx_2&0xFF, 
+			       /*  use bits[15:13] as power of 2 for # of ways */
+			       1 << ((ecx>>13) & 0x7) 
+			       /* Direct and Full associative L2 are very unlikely */);
 			c->x86_cache_size = ecx_2 >> 16;
 		c->x86_tlbsize = ((ebx>>16)&0xff) + ((ebx_2>>16)&0xfff) + 
 			(ebx&0xff) + ((ebx_2)&0xfff);
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/setup64.c linux-2.4.23-pre6/arch/x86_64/kernel/setup64.c
--- linux-2.4.23-pre5/arch/x86_64/kernel/setup64.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/setup64.c	2003-10-01 10:38:45.000000000 -0700
@@ -3,7 +3,7 @@
  * Copyright (C) 1995  Linus Torvalds
  * Copyright 2001, 2002 SuSE Labs / Andi Kleen.
  * See setup.c for older changelog.
- * $Id: setup64.c,v 1.23 2003/05/16 14:22:27 ak Exp $
+ * $Id: setup64.c,v 1.25 2003/07/11 15:58:47 ak Exp $
  */ 
 #include <linux/config.h>
 #include <linux/init.h>
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/signal.c linux-2.4.23-pre6/arch/x86_64/kernel/signal.c
--- linux-2.4.23-pre5/arch/x86_64/kernel/signal.c	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/signal.c	2003-10-01 10:38:45.000000000 -0700
@@ -8,7 +8,7 @@
  *  2000-06-20  Pentium III FXSR, SSE support by Gareth Hughes
  *  2000-2001   x86-64 support by Andi Kleen
  * 
- *  $Id: signal.c,v 1.42 2003/02/24 21:06:02 ak Exp $
+ *  $Id: signal.c,v 1.43 2003/09/06 18:10:44 ak Exp $
  */
 
 #include <linux/sched.h>
@@ -71,6 +71,10 @@
 		default:
 			err |= __put_user(from->si_uid, &to->si_uid);
 			break;
+		case __SI_POLL >> 16:
+			err |= __put_user(from->si_band, &to->si_band); 
+			err |= __put_user(from->si_fd, &to->si_fd); 
+			break;
 		/* case __SI_RT: This is not generated by the kernel as of now.  */
 		}
 		return err;
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/smpboot.c linux-2.4.23-pre6/arch/x86_64/kernel/smpboot.c
--- linux-2.4.23-pre5/arch/x86_64/kernel/smpboot.c	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/smpboot.c	2003-10-01 10:38:45.000000000 -0700
@@ -50,6 +50,7 @@
 #include <asm/kdebug.h>
 #include <asm/timex.h>
 #include <asm/proto.h>
+#include <asm/acpi.h>
 
 /* Setup configured maximum number of CPUs to activate */
 static int max_cpus = -1;
@@ -863,7 +864,7 @@
 	 * If we couldnt find an SMP configuration at boot time,
 	 * get out of here now!
 	 */
-	if (!smp_found_config) {
+	if (!smp_found_config && !acpi_lapic) {
 		printk(KERN_NOTICE "SMP motherboard not detected.\n");
 		io_apic_irqs = 0;
 		cpu_online_map = phys_cpu_present_map = 1;
@@ -907,10 +908,8 @@
 	if (!max_cpus) {
 		smp_found_config = 0;
 		printk(KERN_INFO "SMP mode deactivated, forcing use of dummy APIC emulation.\n");
-		io_apic_irqs = 0;
 		cpu_online_map = phys_cpu_present_map = 1;
 		smp_num_cpus = 1;
-		apic_disabled = 1;
 		goto smp_done;
 	}
 
@@ -1012,6 +1011,9 @@
 	if (cpu_has_tsc && cpucount)
 		synchronize_tsc_bp();
 
+	if (nmi_watchdog != 0) 
+		check_nmi_watchdog(); 
+
 smp_done:
 	zap_low_mappings();
 	time_init_smp();
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/suspend.c linux-2.4.23-pre6/arch/x86_64/kernel/suspend.c
--- linux-2.4.23-pre5/arch/x86_64/kernel/suspend.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/suspend.c	2003-10-01 10:38:45.000000000 -0700
@@ -5,6 +5,9 @@
  *
  * Copyright (c) 2002 Pavel Machek <pavel@suse.cz>
  * Copyright (c) 2001 Patrick Mochel <mochel@osdl.org>
+ * 
+ * AK currently useless for 24 because the core ACPI code doesn't support S3.
+ *    and most of the infrastructure is missing.
  */
 
 #include <linux/config.h>
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/sys_x86_64.c linux-2.4.23-pre6/arch/x86_64/kernel/sys_x86_64.c
--- linux-2.4.23-pre5/arch/x86_64/kernel/sys_x86_64.c	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/sys_x86_64.c	2003-10-01 10:38:45.000000000 -0700
@@ -128,3 +128,17 @@
 	unsigned long raddr;
 	return sys_shmat(shmid,shmaddr,shmflg,&raddr) ?: raddr;
 } 
+
+asmlinkage long sys_time64(long * tloc)
+{
+	struct timeval now; 
+	int i; 
+
+	do_gettimeofday(&now);
+	i = now.tv_sec;
+	if (tloc) {
+		if (put_user(i,tloc))
+			i = -EFAULT;
+	}
+	return i;
+}
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/traps.c linux-2.4.23-pre6/arch/x86_64/kernel/traps.c
--- linux-2.4.23-pre5/arch/x86_64/kernel/traps.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/traps.c	2003-10-01 10:38:45.000000000 -0700
@@ -7,7 +7,7 @@
  *  Pentium III FXSR, SSE support
  *	Gareth Hughes <gareth@valinux.com>, May 2000
  *
- *  $Id: traps.c,v 1.66 2003/06/09 05:18:21 ak Exp $
+ *  $Id: traps.c,v 1.69 2003/09/21 04:54:09 ak Exp $
  */
 
 /*
@@ -81,7 +81,7 @@
 
 struct notifier_block *die_chain;
 
-int kstack_depth_to_print = 40;
+int kstack_depth_to_print = 12;
 
 #ifdef CONFIG_KALLSYMS
 #include <linux/kallsyms.h> 
@@ -342,18 +342,25 @@
 spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
 int die_owner = -1;
 
-void die(const char * str, struct pt_regs * regs, long err)
+void __die(const char * str, struct pt_regs * regs, long err)
+{
+	printk(KERN_EMERG "%s: %04lx\n", str, err & 0xffff);
+ 	notify_die(DIE_OOPS, (char *)str, regs, err, 255, SIGSEGV);
+	show_registers(regs);
+	/* Execute summary in case the oops scrolled away */
+	printk(KERN_EMERG "RIP "); 
+	printk_address(regs->rip); 
+	printk(" RSP <%016lx>\n", regs->rsp); 
+}
+
+void prepare_die(unsigned long *flags)
 {
-	unsigned long flags;
 	int cpu;
 	console_verbose();
 	bust_spinlocks(1);
-	handle_BUG(regs);		
-	printk(KERN_EMERG "%s: %04lx\n", str, err & 0xffff);
- 	notify_die(DIE_OOPS, (char *)str, regs, err, 255, SIGSEGV);
 	cpu = safe_smp_processor_id(); 
 	/* racy, but better than risking deadlock. */ 
-	__save_flags(flags); 
+	__save_flags(*flags); 
 	__cli();
 	if (!spin_trylock(&die_lock)) { 
 		if (cpu == die_owner) 
@@ -362,9 +369,23 @@
 			spin_lock(&die_lock); 
 	}
 	die_owner = cpu; 
-	show_registers(regs);
-	bust_spinlocks(0);
+} 
+
+void exit_die(unsigned long flags)
+{
+	die_owner = -1;
 	spin_unlock_irqrestore(&die_lock, flags);
+	__sti();	/* back scroll should work */
+	bust_spinlocks(0);
+}
+
+void die(const char * str, struct pt_regs * regs, long err)
+{
+	unsigned long flags;
+	prepare_die(&flags);
+	handle_BUG(regs);		
+	__die(str, regs, err);
+	exit_die(flags);
 	do_exit(SIGSEGV);
 }
 
diff -urN linux-2.4.23-pre5/arch/x86_64/kernel/x8664_ksyms.c linux-2.4.23-pre6/arch/x86_64/kernel/x8664_ksyms.c
--- linux-2.4.23-pre5/arch/x86_64/kernel/x8664_ksyms.c	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/kernel/x8664_ksyms.c	2003-10-01 10:38:45.000000000 -0700
@@ -13,6 +13,7 @@
 #include <linux/string.h>
 #include <linux/tty.h>
 #include <linux/string.h>
+#include <linux/nmi.h>
 
 #include <asm/semaphore.h>
 #include <asm/processor.h>
@@ -222,4 +223,6 @@
 #endif
 
 extern void int_ret_from_sys_call(void);
-EXPORT_SYMBOL(int_ret_from_sys_call); 
+EXPORT_SYMBOL_NOVERS(int_ret_from_sys_call); 
+
+EXPORT_SYMBOL(touch_nmi_watchdog);
diff -urN linux-2.4.23-pre5/arch/x86_64/lib/copy_page.S linux-2.4.23-pre6/arch/x86_64/lib/copy_page.S
--- linux-2.4.23-pre5/arch/x86_64/lib/copy_page.S	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/lib/copy_page.S	2003-10-01 10:38:45.000000000 -0700
@@ -10,14 +10,8 @@
 copy_page:
 	prefetch (%rsi) 
 	prefetch 1*64(%rsi)
-	prefetch 2*64(%rsi)
-	prefetch 3*64(%rsi)
-	prefetch 4*64(%rsi)
 	prefetchw (%rdi) 
 	prefetchw 1*64(%rdi) 
-	prefetchw 2*64(%rdi) 
-	prefetchw 3*64(%rdi)
-	prefetchw 4*64(%rdi)
 
 	subq	$3*8,%rsp
 	movq	%rbx,(%rsp)
diff -urN linux-2.4.23-pre5/arch/x86_64/lib/copy_user.S linux-2.4.23-pre6/arch/x86_64/lib/copy_user.S
--- linux-2.4.23-pre5/arch/x86_64/lib/copy_user.S	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/lib/copy_user.S	2003-10-01 10:38:45.000000000 -0700
@@ -4,7 +4,7 @@
  * Functions to copy from and to user space.		
  */		 
 
-#define FIX_ALIGNMENT 1
+/* #define FIX_ALIGNMENT 1 */
 
 	#include <asm/current.h>
 	#include <asm/offset.h>
@@ -59,9 +59,8 @@
 	.globl copy_user_generic	
 	.p2align 4
 copy_user_generic:	
-	/* Put the first cacheline into cache. This should handle
-	   the small movements in ioctls etc., but not penalize the bigger
-	   filesystem data copies too much. */
+	prefetch (%rsi)
+	prefetchw (%rdi)
 	pushq %rbx
 	xorl %eax,%eax		/*zero for the exception handler */
 
diff -urN linux-2.4.23-pre5/arch/x86_64/lib/csum-copy.S linux-2.4.23-pre6/arch/x86_64/lib/csum-copy.S
--- linux-2.4.23-pre5/arch/x86_64/lib/csum-copy.S	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/lib/csum-copy.S	2003-10-01 10:38:45.000000000 -0700
@@ -56,29 +56,18 @@
 	.globl csum_partial_copy_generic
 	.p2align 4
 csum_partial_copy_generic:
-	cmpl	 $3*64,%edx
+	cmpl	 $2*64,%edx
 	jle	 .Lignore
 
 	ignore
-	prefetch (%rdi)
-	ignore
 	prefetch 1*64(%rdi)
 	ignore
 	prefetch 2*64(%rdi)
 	ignore
-	prefetch 3*64(%rdi)
-	ignore
-	prefetch 4*64(%rdi)
-	ignore
 	prefetchw (%rsi)
 	ignore
 	prefetchw 1*64(%rsi)
 	ignore
-	prefetchw 2*64(%rsi)
-	ignore
-	prefetchw 3*64(%rsi)
-	ignore
-	prefetchw 4*64(%rsi)
 
 .Lignore:		
 	subq  $7*8,%rsp
@@ -126,9 +115,6 @@
 	source
 	movq  56(%rdi),%r13
 		
-	ignore 2f
-	prefetch 5*64(%rdi)
-2:							
 	adcq  %rbx,%rax
 	adcq  %r8,%rax
 	adcq  %r11,%rax
@@ -158,10 +144,6 @@
 	dest
 	movq %r13,56(%rsi)
 	
-	ignore 3f
-	prefetchw 5*64(%rsi)
-3:
-	
 	leaq 64(%rdi),%rdi
 	leaq 64(%rsi),%rsi
 
diff -urN linux-2.4.23-pre5/arch/x86_64/lib/usercopy.c linux-2.4.23-pre6/arch/x86_64/lib/usercopy.c
--- linux-2.4.23-pre5/arch/x86_64/lib/usercopy.c	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/lib/usercopy.c	2003-10-01 10:38:45.000000000 -0700
@@ -68,7 +68,7 @@
 	asm volatile(
 		"	testq  %[size8],%[size8]\n"
 		"	jz     4f\n"
-		"0:	movnti %[zero],(%[dst])\n"
+		"0:	movq %[zero],(%[dst])\n"
 		"	addq   %[eight],%[dst]\n"
 		"	decl %%ecx ; jnz   0b\n"
 		"4:	movq  %[size1],%%rcx\n"
@@ -77,7 +77,7 @@
 		"1:	movb   %b[zero],(%[dst])\n"
 		"	incq   %[dst]\n"
 		"	decl %%ecx ; jnz  1b\n"
-		"2:	sfence\n"
+		"2:\n"
 		".section .fixup,\"ax\"\n"
 		"3:	lea 0(%[size1],%[size8],8),%[size8]\n"
 		"	jmp 2b\n"
diff -urN linux-2.4.23-pre5/arch/x86_64/mm/fault.c linux-2.4.23-pre6/arch/x86_64/mm/fault.c
--- linux-2.4.23-pre5/arch/x86_64/mm/fault.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/mm/fault.c	2003-10-01 10:38:45.000000000 -0700
@@ -94,6 +94,85 @@
 	printk("BAD\n");
 }
 
+/* Sometimes the CPU reports invalid exceptions on prefetch.
+   Check that here and ignore.
+   Opcode checker based on code by Richard Brunner */
+static int is_prefetch(struct pt_regs *regs, unsigned long addr)
+{ 
+	unsigned char *instr = (unsigned char *)(regs->rip);
+	int scan_more = 1;
+	int prefetch = 0; 
+	unsigned char *max_instr = instr + 15;
+
+	/* Avoid recursive faults for this common case */
+	if (regs->rip == addr)
+		return 0; 
+
+	if (regs->cs & (1<<2))
+		return 0;
+
+	while (scan_more && instr < max_instr) { 
+		unsigned char opcode;
+		unsigned char instr_hi;
+		unsigned char instr_lo;
+
+		if (__get_user(opcode, instr))
+			break; 
+
+		instr_hi = opcode & 0xf0; 
+		instr_lo = opcode & 0x0f; 
+		instr++;
+
+		switch (instr_hi) { 
+		case 0x20:
+		case 0x30:
+			/* Values 0x26,0x2E,0x36,0x3E are valid x86
+			   prefixes.  In long mode, the CPU will signal
+			   invalid opcode if some of these prefixes are
+			   present so we will never get here anyway */
+			scan_more = ((instr_lo & 7) == 0x6);
+			break;
+			
+		case 0x40:
+			/* In AMD64 long mode, 0x40 to 0x4F are valid REX prefixes
+			   Need to figure out under what instruction mode the
+			   instruction was issued ... */
+			/* Could check the LDT for lm, but for now it's good
+			   enough to assume that long mode only uses well known
+			   segments or kernel. */
+			scan_more = ((regs->cs & 3) == 0) || (regs->cs == __USER_CS);
+			break;
+			
+		case 0x60:
+			/* 0x64 thru 0x67 are valid prefixes in all modes. */
+			scan_more = (instr_lo & 0xC) == 0x4;
+			break;		
+		case 0xF0:
+			/* 0xF0, 0xF2, and 0xF3 are valid prefixes in all modes. */
+			scan_more = !instr_lo || (instr_lo>>1) == 1;
+			break;			
+		case 0x00:
+			/* Prefetch instruction is 0x0F0D or 0x0F18 */
+			scan_more = 0;
+			if (__get_user(opcode, instr)) 
+				break;
+			prefetch = (instr_lo == 0xF) &&
+				(opcode == 0x0D || opcode == 0x18);
+			break;			
+		default:
+			scan_more = 0;
+			break;
+		} 
+	}
+
+#if 0
+	if (prefetch)
+		printk("%s: prefetch caused page fault at %lx/%lx\n", current->comm,
+		       regs->rip, addr);
+#endif
+	return prefetch;
+}
+
 int page_fault_trace; 
 int exception_trace = 1;
 
@@ -156,7 +235,7 @@
 	 * context, we must not take the fault..
 	 */
 	if (in_interrupt() || !mm)
-		goto no_context;
+		goto bad_area_nosemaphore;
 
 again:
 	down_read(&mm->mmap_sem);
@@ -226,9 +305,11 @@
 	up_read(&mm->mmap_sem);
 
 bad_area_nosemaphore:
-
 	/* User mode accesses just cause a SIGSEGV */
 	if (error_code & 4) {
+		if (is_prefetch(regs, address))
+			return;
+
 		if (exception_trace && !(tsk->ptrace & PT_PTRACED) && 
 		    (tsk->sig->action[SIGSEGV-1].sa.sa_handler == SIG_IGN ||
 		    (tsk->sig->action[SIGSEGV-1].sa.sa_handler == SIG_DFL)))
@@ -260,40 +341,27 @@
 		return;
 	}
 
+	if (is_prefetch(regs, address))
+		return;
+
 /*
  * Oops. The kernel tried to access some bad page. We'll have to
  * terminate things with extreme prejudice.
  */
 
-	console_verbose();
-	bust_spinlocks(1); 
-
-	int cpu = safe_smp_processor_id(); 
 	unsigned long flags; 
-	extern int die_owner; 
-	extern spinlock_t die_lock;
-	
-	__save_flags(flags); 
-	__cli(); 
-	if (!spin_trylock(&die_lock)) { 
-		if (cpu == die_owner) 
-			/* nested oops. should stop eventually */;
-		else
-			spin_lock(&die_lock); 
-	}
-	die_owner = cpu; 
-
+	prepare_die(&flags);
 	if (address < PAGE_SIZE)
 		printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
 	else
 		printk(KERN_ALERT "Unable to handle kernel paging request");
-	printk(" at virtual address %016lx\n",address);
-	printk(" printing rip:\n");
-	printk("%016lx\n", regs->rip);
+	printk(KERN_ALERT " at %016lx RIP: ", address); 
+	printk_address(regs->rip);
 	dump_pagetable(address);
-	die("Oops", regs, error_code);
-	bust_spinlocks(0); 
-	spin_unlock_irqrestore(&die_lock, flags);
+	__die("Oops", regs, error_code);
+	/* Executive summary in case the oops scrolled away */
+	printk(KERN_EMERG "CR2: %016lx\n", address);
+	exit_die(flags);
 	do_exit(SIGKILL);
 
 /*
@@ -315,10 +383,13 @@
 do_sigbus:
 	up_read(&mm->mmap_sem);
 
-	/*
-	 * Send a sigbus, regardless of whether we were in kernel
-	 * or user mode.
-	 */
+	/* Kernel mode? Handle exceptions or die */
+	if (!(error_code & 4))
+		goto no_context;
+		
+	if (is_prefetch(regs, address))
+		return;
+
 	tsk->thread.cr2 = address;
 	tsk->thread.error_code = error_code;
 	tsk->thread.trap_no = 14;
@@ -327,10 +398,6 @@
 	info.si_code = BUS_ADRERR;
 	info.si_addr = (void *)address;
 	force_sig_info(SIGBUS, &info, tsk);
-
-	/* Kernel mode? Handle exceptions or die */
-	if (!(error_code & 4))
-		goto no_context;
 	return;
 
 
diff -urN linux-2.4.23-pre5/arch/x86_64/mm/ioremap.c linux-2.4.23-pre6/arch/x86_64/mm/ioremap.c
--- linux-2.4.23-pre5/arch/x86_64/mm/ioremap.c	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/mm/ioremap.c	2003-10-01 10:38:45.000000000 -0700
@@ -125,12 +125,12 @@
 	 */
 	if (phys_addr < virt_to_phys(high_memory)) {
 		char *t_addr, *t_end;
-		struct page *page;
 
 		t_addr = __va(phys_addr);
 		t_end = t_addr + (size - 1);
 	   
 #ifndef CONFIG_DISCONTIGMEM
+		struct page *page;
 		for(page = virt_to_page(t_addr); page <= virt_to_page(t_end); page++)
 			if(!PageReserved(page))
 				return NULL;
diff -urN linux-2.4.23-pre5/arch/x86_64/mm/k8topology.c linux-2.4.23-pre6/arch/x86_64/mm/k8topology.c
--- linux-2.4.23-pre5/arch/x86_64/mm/k8topology.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/arch/x86_64/mm/k8topology.c	2003-10-01 10:38:45.000000000 -0700
@@ -6,7 +6,7 @@
  * Instead the northbridge registers are read directly. 
  * 
  * Copyright 2002 Andi Kleen, SuSE Labs.
- * $Id: k8topology.c,v 1.7 2003/04/02 21:36:22 ak Exp $
+ * $Id: k8topology.c,v 1.11 2003/09/12 01:55:37 ak Exp $
  */
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -20,6 +20,8 @@
 #include <asm/e820.h>
 #include <asm/pci-direct.h>
 
+#define Dprintk(x...)
+
 int memnode_shift;
 u8  memnodemap[NODEMAPSIZE];
 
@@ -110,6 +112,9 @@
 		limit = read_pci_config(0, nb, 1, 0x44 + i*8);
 
 		nodeid = limit & 3; 
+		if ((base & 3) == 0) { 
+			continue;
+		} 
 		if (!limit) { 
 			printk(KERN_INFO "Skipping node entry %d (base %lx)\n", i,			       base);
 			continue;
@@ -145,7 +150,7 @@
 		if (limit == base) 
 			continue; 
 		if (limit < base) { 
-			printk(KERN_INFO"Node %d bogus settings %lx-%lx. Ignored.\n",
+			Dprintk(KERN_INFO"Node %d bogus settings %lx-%lx. Ignored.\n",
 			       nodeid, base, limit); 			       
 			continue; 
 		} 
diff -urN linux-2.4.23-pre5/drivers/acpi/Config.in linux-2.4.23-pre6/drivers/acpi/Config.in
--- linux-2.4.23-pre5/drivers/acpi/Config.in	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/acpi/Config.in	2003-10-01 10:38:45.000000000 -0700
@@ -10,36 +10,30 @@
   if [ "$CONFIG_ACPI" = "y" ]; then
 
     define_bool CONFIG_ACPI_BOOT		y
-
-    if [ "$CONFIG_SMP" = "y" ]; then
-      bool         'CPU Enumeration Only' CONFIG_ACPI_HT_ONLY
-    else
-      define_bool CONFIG_ACPI_HT_ONLY n
+    define_bool CONFIG_ACPI_BUS			y
+    define_bool CONFIG_ACPI_INTERPRETER		y
+    define_bool CONFIG_ACPI_EC			y
+    define_bool CONFIG_ACPI_POWER		y
+    if [ "$CONFIG_PCI" = "y" ]; then
+      define_bool CONFIG_ACPI_PCI		y
     fi
-
-    if [ "$CONFIG_ACPI_HT_ONLY" = "n" ]; then
-      define_bool CONFIG_ACPI_BUS		y
-      define_bool CONFIG_ACPI_INTERPRETER	y
-      define_bool CONFIG_ACPI_EC		y
-      define_bool CONFIG_ACPI_POWER		y
-      if [ "$CONFIG_PCI" = "y" ]; then
-        define_bool CONFIG_ACPI_PCI		y
-      fi
-      define_bool CONFIG_ACPI_SLEEP		y
-      define_bool CONFIG_ACPI_SYSTEM		y
-      tristate     '  AC Adapter'	CONFIG_ACPI_AC
-      tristate     '  Battery'		CONFIG_ACPI_BATTERY
-      tristate     '  Button'		CONFIG_ACPI_BUTTON
-      tristate     '  Fan'		CONFIG_ACPI_FAN
-      tristate     '  Processor'	CONFIG_ACPI_PROCESSOR
-      dep_tristate '  Thermal Zone' 	CONFIG_ACPI_THERMAL $CONFIG_ACPI_PROCESSOR
-      if [ "$CONFIG_NUMA" = "y" -a "$CONFIG_X86_64" != "y" ]; then
-        dep_bool       '  NUMA support'     CONFIG_ACPI_NUMA $CONFIG_NUMA
-      fi
-      tristate     '  ASUS Laptop Extras'  CONFIG_ACPI_ASUS
-      tristate     '  Toshiba Laptop Extras'	CONFIG_ACPI_TOSHIBA
-      bool         '  Debug Statements'	CONFIG_ACPI_DEBUG
-      bool         '  Relaxed AML Checking'	CONFIG_ACPI_RELAXED_AML
+    define_bool CONFIG_ACPI_SLEEP		y
+    define_bool CONFIG_ACPI_SYSTEM		y
+    tristate     '  AC Adapter'	CONFIG_ACPI_AC
+    tristate     '  Battery'		CONFIG_ACPI_BATTERY
+    tristate     '  Button'		CONFIG_ACPI_BUTTON
+    tristate     '  Fan'		CONFIG_ACPI_FAN
+    tristate     '  Processor'	CONFIG_ACPI_PROCESSOR
+    dep_tristate '  Thermal Zone' 	CONFIG_ACPI_THERMAL $CONFIG_ACPI_PROCESSOR
+    if [ "$CONFIG_NUMA" = "y" -a "$CONFIG_X86_64" != "y" ]; then
+      dep_bool       '  NUMA support'     CONFIG_ACPI_NUMA $CONFIG_NUMA
+    fi
+    tristate     '  ASUS Laptop Extras'  CONFIG_ACPI_ASUS
+    tristate     '  Toshiba Laptop Extras'	CONFIG_ACPI_TOSHIBA
+    bool         '  Debug Statements'	CONFIG_ACPI_DEBUG
+    bool         '  Relaxed AML Checking'	CONFIG_ACPI_RELAXED_AML
+  else if [ "$CONFIG_SMP" = "y" ]; then
+      define_bool CONFIG_ACPI_BOOT		y
     fi
   fi
 
diff -urN linux-2.4.23-pre5/drivers/acpi/asus_acpi.c linux-2.4.23-pre6/drivers/acpi/asus_acpi.c
--- linux-2.4.23-pre5/drivers/acpi/asus_acpi.c	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.23-pre6/drivers/acpi/asus_acpi.c	2003-10-01 10:38:45.000000000 -0700
@@ -26,13 +26,17 @@
  *  Johann Wiesner - Small compile fixes
  *  John Belmonte  - ACPI code for Toshiba laptop was a good starting point.
  *
- *  TODO
+ *  TODO:
  *  add Fn key status
- *  Add mode selection on module loading (parameter) -> still necessary ?
+ *  Add mode selection on module loading (parameter) -> still necessary?
  *  Complete display switching -- may require dirty hacks?
- *
  */
 
+#include <linux/config.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) && defined (MODULE)
+#include <linux/modversions.h>
+#endif
+
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
@@ -41,7 +45,7 @@
 #include <acpi/acpi_drivers.h>
 #include <acpi/acpi_bus.h>
 
-#define ASUS_ACPI_VERSION "0.24a"
+#define ASUS_ACPI_VERSION "0.26"
 
 #define PROC_ASUS       "asus"	//the directory
 #define PROC_MLED       "mled"
@@ -73,7 +77,6 @@
 MODULE_LICENSE("GPL");
 EXPORT_NO_SYMBOLS;
 
-
 static uid_t asus_uid = 0;
 static gid_t asus_gid = 0;
 MODULE_PARM(asus_uid, "i");
@@ -111,20 +114,24 @@
 	struct model_data *methods;	//methods available on the laptop
 	u8 brightness;			//brighness level
 	enum {
-		L2X = 0,	//L200D -> TODO check Q11 (Fn+F8)
-				//	   Calling this method simply hang the
+		A1X=0,  	//A1340D, A1300F
+		A2X,		//A2500H
+		D1X,		//D1
+		L1X,		//L1400B
+		L2X,		//L2000D -> TODO check Q11 (Fn+F8)
+				//	   Calling this method simply hangs the
 				//	   computer, ISMI method hangs the laptop.
-		L3X,		//L3C
 		L3D,		//L3400D
+		L3X,		//L3C
+		L5X,		//L5C TODO this model seems to have one more
+		                //         LED, add support
 		M2X,		//M2400E
+		M3N,		//M3700N, but also S1300N -> TODO WLED
 		S1X,		//S1300A -> TODO special keys do not work ?
-		D1X,		//D1
-		L1X,		//L1400B
-		A1X, 		//A1340D, A1300F
-		J1X,		//S200 (J1)
-				//TODO  A1370D does not seems to have a ATK device 
+		S2X,		//S200 (J1 reported), Victor MP-XP7210
+				//TODO  A1370D does not seem to have an ATK device 
 				//	L8400 model doesn't have ATK
-		END_MODEL,
+		END_MODEL
 	} model;		//Models currently supported
 	u16 event_count[128];	//count for each event TODO make this better
 };
@@ -134,7 +141,8 @@
 #define S1X_PREFIX "\\_SB.PCI0.PX40."
 #define L1X_PREFIX S1X_PREFIX
 #define A1X_PREFIX "\\_SB.PCI0.ISA.EC0."
-#define J1X_PREFIX A1X_PREFIX
+#define S2X_PREFIX A1X_PREFIX
+#define M3N_PREFIX "\\_SB.PCI0.SBRG.EC0."
 
 static struct model_data model_conf[END_MODEL] = {
         /*
@@ -148,33 +156,43 @@
 	 * it seems to be a kind of switch, but what for ?
 	 *
 	 */
+	{"A1X", "MLED", "\\MAIL", NULL, NULL, A1X_PREFIX "_Q10", "\\BKLI",
+	 A1X_PREFIX "_Q0E", A1X_PREFIX "_Q0F", NULL, NULL, NULL, NULL, NULL},
+
+	{"A2X", "MLED", NULL, "WLED", "\\SG66", "\\Q10", "\\BAOF",
+	 "\\Q0E", "\\Q0F", "SPLV", "GPLV", "\\CMOD", "SDSP", "\\INFB"},
+
+	{"D1X", "MLED", NULL, NULL, NULL, "\\Q0D", "\\GP11", 
+	 "\\Q0C", "\\Q0B", NULL, NULL, "\\BLVL", "SDSP","\\INFB"},
+
+	{"L1X", "MLED", NULL, "WLED", NULL, L1X_PREFIX "Q10", "\\PNOF", 
+	 L1X_PREFIX "Q0F", L1X_PREFIX "Q0E", "SPLV", "GPLV", "\\BRIT", NULL, NULL},
+	 
 	{"L2X", "MLED", "\\SGP6", "WLED", "\\RCP3", "\\Q10", "\\SGP0", 
 	 "\\Q0E", "\\Q0F", NULL, NULL, NULL, "SDSP", "\\INFB"},
 
+	{"L3D", "MLED", "\\MALD", "WLED", NULL, "\\Q10", "\\BKLG",
+	 "\\Q0E", "\\Q0F", "SPLV", "GPLV", "\\BLVL", "SDSP", "\\INFB"},
+
 	{"L3X", "MLED", NULL, "WLED", NULL, L3X_PREFIX "_Q10", "\\GL32", 
 	 L3X_PREFIX "_Q0F", L3X_PREFIX "_Q0E", "SPLV", "GPLV", "\\BLVL", "SDSP", 
 	 "\\_SB.PCI0.PCI1.VGAC.NMAP"},
 
-	{"L3D", "MLED", "\\MALD", "WLED", NULL, "\\Q10", "\\BKLG",
-	 "\\Q0E", "\\Q0F", "SPLV", "GPLV", "\\BLVL", "SDSP", "\\INFB"},
-
+	{"L5X", "MLED", NULL, "WLED", "WRED", "\\Q0D", "\\BAOF", 
+	 "\\Q0C","\\Q0B", "SPLV", "GPLV", NULL, "SDSP", "\\INFB"},
+	 
 	{"M2X", "MLED", NULL, "WLED", NULL, "\\Q10", "\\GP06", 
 	 "\\Q0E","\\Q0F", "SPLV", "GPLV", NULL, "SDSP", "\\INFB"},
+
+	{"M3N", "MLED", NULL, "WLED", "\\PO33", M3N_PREFIX "_Q10", "\\BKLT", 
+	 M3N_PREFIX "_Q0F", M3N_PREFIX "_Q0E", "SPLV", "GPLV", "\\LBTN", "SDSP", 
+	 "\\ADVG"},
 	
 	{"S1X", "MLED", "\\EMLE", "WLED", NULL, S1X_PREFIX "Q10", "\\PNOF", 
 	 S1X_PREFIX "Q0F", S1X_PREFIX "Q0E", "SPLV", "GPLV", "\\BRIT", NULL, NULL},
 	
-	{"D1X", "MLED", NULL, NULL, NULL, "\\Q0D", "\\GP11", 
-	 "\\Q0C", "\\Q0B", NULL, NULL, "\\BLVL", "SDSP","\\INFB"},
-
-	{"L1X", "MLED", NULL, "WLED", NULL, L1X_PREFIX "Q10", "\\PNOF", 
-	 L1X_PREFIX "Q0F", L1X_PREFIX "Q0E", "SPLV", "GPLV", "\\BRIT", NULL, NULL},
-
-	{"A1X", "MLED", "\\MAIL", NULL, NULL, A1X_PREFIX "_Q10", "\\BKLI",
-	 A1X_PREFIX "_Q0E", A1X_PREFIX "_Q0F", NULL, NULL, NULL, NULL, NULL},
-
-	{"J1X", "MLED", "\\MAIL", NULL, NULL, J1X_PREFIX "_Q10", "\\BKLI",
-	 J1X_PREFIX "_Q0B", J1X_PREFIX "_Q0A", NULL, NULL, NULL, NULL, NULL}
+	{"S2X", "MLED", "\\MAIL", NULL, NULL, S2X_PREFIX "_Q10", "\\BKLI",
+	 S2X_PREFIX "_Q0B", S2X_PREFIX "_Q0A", NULL, NULL, NULL, NULL, NULL}
 };
 
 /* procdir we use */
@@ -235,7 +253,7 @@
 	output.length = sizeof(out_obj);
 	output.pointer = &out_obj;
 
-	status = acpi_evaluate_object(handle, (char*) method, NULL, &output);
+	status = acpi_evaluate_object(handle, (char *) method, NULL, &output);
 	*val = out_obj.integer.value;
 	return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER);
 }
@@ -250,6 +268,7 @@
 		void *data)
 {
 	int len = 0;
+	int sfun;
 	struct asus_hotk *hotk = (struct asus_hotk *) data;
 	char buf[16];		//enough for all info
 	/*
@@ -258,28 +277,27 @@
 	 */
 
 	len += sprintf(page, ACPI_HOTK_NAME " " ASUS_ACPI_VERSION "\n");
-	len +=
-	    sprintf(page + len, "Model reference    : %s\n",
-		    hotk->methods->name);
+	len += sprintf(page + len, "Model reference    : %s\n", 
+		       hotk->methods->name);
+	if(read_acpi_int(hotk->handle, "SFUN", &sfun))
+		len += sprintf(page + len, "SFUN value         : 0x%04x\n", sfun);
 	if (asus_info) {
-		snprintf(buf, 5, "%s", asus_info->signature);
-		len += sprintf(page + len, "ACPI signature     : %s\n", buf);
 		snprintf(buf, 16, "%d", asus_info->length);
-		len += sprintf(page + len, "Table length       : %s\n", buf);
-		snprintf(buf, 16, "%d", asus_info->revision);
-		len += sprintf(page + len, "ACPI minor version : %s\n", buf);
+		len += sprintf(page + len, "DSDT length        : %s\n", buf);
 		snprintf(buf, 16, "%d", asus_info->checksum);
-		len += sprintf(page + len, "Checksum           : %s\n", buf);
+		len += sprintf(page + len, "DSDT checksum      : %s\n", buf);
+		snprintf(buf, 16, "%d", asus_info->revision);
+		len += sprintf(page + len, "DSDT revision      : %s\n", buf);
 		snprintf(buf, 7, "%s", asus_info->oem_id);
-		len += sprintf(page + len, "OEM identification : %s\n", buf);
+		len += sprintf(page + len, "OEM id             : %s\n", buf);
 		snprintf(buf, 9, "%s", asus_info->oem_table_id);
 		len += sprintf(page + len, "OEM table id       : %s\n", buf);
 		snprintf(buf, 16, "%x", asus_info->oem_revision);
-		len += sprintf(page + len, "OEM rev number     : 0x%s\n", buf);
+		len += sprintf(page + len, "OEM revision       : 0x%s\n", buf);
 		snprintf(buf, 5, "%s", asus_info->asl_compiler_id);
-		len += sprintf(page + len, "ASL comp vendor ID : %s\n", buf);
+		len += sprintf(page + len, "ASL comp vendor id : %s\n", buf);
 		snprintf(buf, 16, "%x", asus_info->asl_compiler_revision);
-		len += sprintf(page + len, "ASL comp rev number: 0x%s\n", buf);
+		len += sprintf(page + len, "ASL comp revision  : 0x%s\n", buf);
 	}
 
 	return len;
@@ -305,7 +323,7 @@
 				  &led_status))
 			len =  sprintf(page, "%d\n", led_status);
 		else
-			printk(KERN_NOTICE "Asus ACPI: Error reading MLED "
+			printk(KERN_WARNING "Asus ACPI: Error reading MLED "
 			       "status\n");
 	} else {
 		len = sprintf(page, "%d\n", (hotk->status & MLED_ON) ? 1 : 0);
@@ -335,7 +353,7 @@
 	/* We don't have to check mt_mled exists if we are here :) */
 	if (!write_acpi_int(hotk->handle, hotk->methods->mt_mled, led_out,
 			    NULL))
-		printk(KERN_NOTICE "Asus ACPI: MLED write failed\n");
+		printk(KERN_WARNING "Asus ACPI: MLED write failed\n");
 
 
 
@@ -356,11 +374,11 @@
 	int led_status;
 
 	if (hotk->methods->wled_status) {
-		if (read_acpi_int(NULL, hotk->methods->mled_status, 
+		if (read_acpi_int(NULL, hotk->methods->wled_status, 
 				  &led_status))
 			len = sprintf(page, "%d\n", led_status);
 		else
-			printk(KERN_NOTICE "Asus ACPI: Error reading WLED "
+			printk(KERN_WARNING "Asus ACPI: Error reading WLED "
 			       "status\n");
 	} else {
 		len = sprintf(page, "%d\n", (hotk->status & WLED_ON) ? 1 : 0);
@@ -387,7 +405,7 @@
 	/* We don't have to check if mt_wled exists if we are here :) */
 	if (!write_acpi_int(hotk->handle, hotk->methods->mt_wled, led_out,
 			    NULL))
-		printk(KERN_NOTICE "Asus ACPI: WLED write failed\n");
+		printk(KERN_WARNING "Asus ACPI: WLED write failed\n");
 
 
 	return count;
@@ -400,7 +418,7 @@
 
 	/* We don't have to check anything, if we are here */
 	if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd))
-		printk(KERN_NOTICE "Asus ACPI: Error reading LCD status\n");
+		printk(KERN_WARNING "Asus ACPI: Error reading LCD status\n");
 	
 	if (hotk->model == L2X)
 		lcd = ~lcd;
@@ -439,7 +457,7 @@
 		    acpi_evaluate_object(NULL, hotk->methods->mt_lcd_switch,
 					 NULL, NULL);
 		if (ACPI_FAILURE(status))
-			printk(KERN_NOTICE "Asus ACPI: Error switching LCD\n");
+			printk(KERN_WARNING "Asus ACPI: Error switching LCD\n");
 	}
 
 	return count;
@@ -453,15 +471,15 @@
 {
 	acpi_status status = 0;
 
-	/* ATKD laptop */
+	/* SPLV laptop */
 	if(hotk->methods->brightness_set) {
 		if (!write_acpi_int(hotk->handle, hotk->methods->brightness_set, 
 				    value, NULL))
-			printk(KERN_NOTICE "Asus ACPI: Error changing brightness\n");
+			printk(KERN_WARNING "Asus ACPI: Error changing brightness\n");
 		return;
 	}
 
-	/* HOTK laptop if we are here, act as appropriate */
+	/* No SPLV method if we are here, act as appropriate */
 	value -= hotk->brightness;
 	while (value != 0) {
 		status = acpi_evaluate_object(NULL, (value > 0) ? 
@@ -470,7 +488,7 @@
 					      NULL, NULL);
 		(value > 0) ? value-- : value++;
 		if (ACPI_FAILURE(status))
-			printk(KERN_NOTICE "Asus ACPI: Error changing brightness\n");
+			printk(KERN_WARNING "Asus ACPI: Error changing brightness\n");
 	}
 	return;
 }
@@ -479,15 +497,15 @@
 {
 	int value;
 	
-	if(hotk->methods->brightness_get) { /* ATKD laptop */
+	if(hotk->methods->brightness_get) { /* SPLV/GPLV laptop */
 		if (!read_acpi_int(hotk->handle, hotk->methods->brightness_get, 
 				   &value))
-			printk(KERN_NOTICE "Asus ACPI: Error reading brightness\n");
+			printk(KERN_WARNING "Asus ACPI: Error reading brightness\n");
 	} else if (hotk->methods->brightness_status) { /* For D1 for example */
 		if (!read_acpi_int(NULL, hotk->methods->brightness_status, 
 				   &value))
-			printk(KERN_NOTICE "Asus ACPI: Error reading brightness\n");
-	} else /* HOTK laptop */
+			printk(KERN_WARNING "Asus ACPI: Error reading brightness\n");
+	} else /* No GPLV method */
 		value = hotk->brightness;
 	return value;
 }
@@ -513,7 +531,7 @@
 			/* 0 <= value <= 15 */
 		set_brightness(value, hotk);
 	} else {
-		printk(KERN_NOTICE "Asus ACPI: Error reading user input\n");
+		printk(KERN_WARNING "Asus ACPI: Error reading user input\n");
 	}
 
 	return count;
@@ -524,7 +542,7 @@
 	/* no sanity check needed for now */
 	if (!write_acpi_int(hotk->handle, hotk->methods->display_set, 
 			    value, NULL))
-		printk(KERN_NOTICE "Asus ACPI: Error setting display\n");
+		printk(KERN_WARNING "Asus ACPI: Error setting display\n");
 	return;
 }
 
@@ -541,12 +559,12 @@
 	struct asus_hotk *hotk = (struct asus_hotk *) data;
 	
 	if (!read_acpi_int(hotk->handle, hotk->methods->display_get, &value))
-		printk(KERN_NOTICE "Asus ACPI: Error reading display status\n");
+		printk(KERN_WARNING "Asus ACPI: Error reading display status\n");
 	return sprintf(page, "%d\n", value);
 }
 
 /*
- * Preliminary support for display switching. As of now: 0x01 should activate 
+ * Experimental support for display switching. As of now: 0x01 should activate 
  * the LCD output, 0x02 should do for CRT, and 0x04 for TV-Out. Any combination 
  * (bitwise) of these will suffice. I never actually tested 3 displays hooked up 
  * simultaneously, so be warned.
@@ -563,13 +581,13 @@
 	if (sscanf(buffer, "%d", &value) == 1)
 		set_display(value, hotk);
 	else {
-		printk(KERN_NOTICE "Asus ACPI: Error reading user input\n");
+		printk(KERN_WARNING "Asus ACPI: Error reading user input\n");
 	}
 
 	return count;
 }
 
-static int asus_hotk_add_fs(struct acpi_device *device)
+static int __init asus_hotk_add_fs(struct acpi_device *device)
 {
 	struct proc_dir_entry *proc;
 	struct asus_hotk *hotk = acpi_driver_data(device);
@@ -583,7 +601,7 @@
 
 	if ((asus_uid == 0) && (asus_gid == 0)){
 		mode = S_IFREG | S_IRUGO | S_IWUGO;
-	}else{
+	} else {
 		mode = S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP;
 	}
 
@@ -599,7 +617,7 @@
 		proc->uid = asus_uid;
 		proc->gid = asus_gid;;
 	} else {
-		printk(KERN_NOTICE "  Unable to create " PROC_INFOS
+		printk(KERN_WARNING "  Unable to create " PROC_INFOS
 		       " fs entry\n");
 	}
 
@@ -613,7 +631,7 @@
 			proc->uid = asus_uid;
 			proc->gid = asus_gid;;
 		} else {
-			printk(KERN_NOTICE "  Unable to create " PROC_WLED
+			printk(KERN_WARNING "  Unable to create " PROC_WLED
 			       " fs entry\n");
 		}
 	}
@@ -628,7 +646,7 @@
 			proc->uid = asus_uid;
 			proc->gid = asus_gid;;
 		} else {
-			printk(KERN_NOTICE "  Unable to create " PROC_MLED
+			printk(KERN_WARNING "  Unable to create " PROC_MLED
 			       " fs entry\n");
 		}
 	}
@@ -647,7 +665,7 @@
 			proc->uid = asus_uid;
 			proc->gid = asus_gid;;
 		} else {
-			printk(KERN_NOTICE "  Unable to create " PROC_LCD
+			printk(KERN_WARNING "  Unable to create " PROC_LCD
 			       " fs entry\n");
 		}
 	}
@@ -663,7 +681,7 @@
 			proc->uid = asus_uid;
 			proc->gid = asus_gid;;
 		} else {
-			printk(KERN_NOTICE "  Unable to create " PROC_BRN
+			printk(KERN_WARNING "  Unable to create " PROC_BRN
 			       " fs entry\n");
 		}
 	}
@@ -678,19 +696,19 @@
 			proc->uid = asus_uid;
 			proc->gid = asus_gid;;
 		} else {
-			printk(KERN_NOTICE "  Unable to create " PROC_DISP
+			printk(KERN_WARNING "  Unable to create " PROC_DISP
 			       " fs entry\n");
 		}
 	}
 
-	return (AE_OK);
+	return 0;
 }
 
 
 static void asus_hotk_notify(acpi_handle handle, u32 event, void *data)
 {
 	/* TODO Find a better way to handle events count. Here, in data, we receive
-	 * the hotk, so we can make anything !!
+	 * the hotk, so we can do anything!
 	 */
 	struct asus_hotk *hotk = (struct asus_hotk *) data;
 
@@ -713,19 +731,40 @@
  * This function is used to initialize the hotk with right values. In this
  * method, we can make all the detection we want, and modify the hotk struct
  */
-static int asus_hotk_get_info(struct asus_hotk *hotk)
+static int __init asus_hotk_get_info(struct asus_hotk *hotk)
 {
 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	struct acpi_buffer dsdt = { ACPI_ALLOCATE_BUFFER, NULL };
 	union acpi_object *model = NULL;
+	int bsts_result;
+	acpi_status status;
 
-	/* 
-	 * We have to write 0 on init this far for all ASUS models
+	/*
+	 * Get DSDT headers early enough to allow for differentiating between 
+	 * models, but late enough to allow acpi_bus_register_driver() to fail 
+	 * before doing anything ACPI-specific. Should we encounter a machine,
+	 * which needs special handling (i.e. its hotkey device has a different
+	 * HID), this bit will be moved. A global variable asus_info contains
+	 * the DSDT header.
 	 */
+	status = acpi_get_table(ACPI_TABLE_DSDT, 1, &dsdt);
+	if (ACPI_FAILURE(status))
+		printk(KERN_WARNING "  Couldn't get the DSDT table header\n");
+	else
+		asus_info = (struct acpi_table_header *) dsdt.pointer;
+
+	/* We have to write 0 on init this far for all ASUS models */
 	if (!write_acpi_int(hotk->handle, "INIT", 0, &buffer)) {
-		printk(KERN_NOTICE "  Hotkey initialization failed\n");
+		printk(KERN_ERR "  Hotkey initialization failed\n");
 		return -ENODEV;
 	}
 
+	/* For testing purposes */
+	if (!read_acpi_int(hotk->handle, "BSTS", &bsts_result))
+		printk(KERN_WARNING "  Error calling BSTS\n");
+	else if (bsts_result)
+		printk(KERN_NOTICE "  BSTS called, 0x%02x returned\n", bsts_result);
+
 	/*
 	 * Here, we also use asus_info to make decision. For example, on INIT
 	 * method, S1X and L1X models both reports to be L84F, but they don't
@@ -750,26 +789,34 @@
 		hotk->model = L3X;
 	else if (strncmp(model->string.pointer, "M2", 2) == 0)
 		hotk->model = M2X;
+	else if (strncmp(model->string.pointer, "M3N", 3) == 0 ||
+		 strncmp(model->string.pointer, "S1N", 3) == 0)
+		hotk->model = M3N; /* S1300N is similar enough */
 	else if (strncmp(model->string.pointer, "L2", 2) == 0)
 		hotk->model = L2X;
-	else if (strncmp(model->string.pointer, "L8", 2) == 0)
+	else if (strncmp(model->string.pointer, "L8", 2) == 0) {
 		/* S1300A reports L84F, but L1400B too */
-		if (strncmp(asus_info->oem_table_id, "L1", 2) == 0)
-			hotk->model = L1X;
-		else
+		if (asus_info) {
+			if (strncmp(asus_info->oem_table_id, "L1", 2) == 0)
+				hotk->model = L1X;
+		} else
 			hotk->model = S1X;
+	}
 	else if (strncmp(model->string.pointer, "D1", 2) == 0)
 		hotk->model = D1X;
 	else if (strncmp(model->string.pointer, "A1", 2) == 0)
 		hotk->model = A1X;
+	else if (strncmp(model->string.pointer, "A2", 2) == 0)
+		hotk->model = A2X;
 	else if (strncmp(model->string.pointer, "J1", 2) == 0)
-		hotk->model = J1X;
-
+		hotk->model = S2X;
+	else if (strncmp(model->string.pointer, "L5", 2) == 0)
+		hotk->model = L5X;
 
 	if (hotk->model == END_MODEL) {
 		/* By default use the same values, as I don't know others */
-		printk("unsupported, trying default values, contact the "
-		       "developers\n");
+		printk("unsupported, trying default values, supply the "
+		       "developers with your DSDT\n");
 		hotk->model = L2X;
 	} else {
 		printk("supported\n");
@@ -784,7 +831,7 @@
 
 
 
-static int asus_hotk_check(struct asus_hotk *hotk)
+static int __init asus_hotk_check(struct asus_hotk *hotk)
 {
 	int result = 0;
 
@@ -798,7 +845,7 @@
 	if (hotk->device->status.present) {
 		result = asus_hotk_get_info(hotk);
 	} else {
-		printk(KERN_NOTICE "  Hotkey device not present, aborting\n");
+		printk(KERN_ERR "  Hotkey device not present, aborting\n");
 		return(-EINVAL);
 	}
 
@@ -807,7 +854,7 @@
 
 
 
-static int asus_hotk_add(struct acpi_device *device)
+static int __init asus_hotk_add(struct acpi_device *device)
 {
 	struct asus_hotk *hotk = NULL;
 	acpi_status status = AE_OK;
@@ -816,6 +863,9 @@
 	if (!device)
 		return(-EINVAL);
 
+	printk(KERN_NOTICE "Asus Laptop ACPI Extras version %s\n",
+	       ASUS_ACPI_VERSION);
+
 	hotk =
 	    (struct asus_hotk *) kmalloc(sizeof(struct asus_hotk), GFP_KERNEL);
 	if (!hotk)
@@ -843,25 +893,23 @@
 	 */
 	status = acpi_install_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY,
 					     asus_hotk_notify, hotk);
-	if (ACPI_FAILURE(status)) {
-		printk(KERN_NOTICE
-		       "  Error installing notify handler\n");
-	} else {
-		printk(KERN_DEBUG
-		       "  Notify Handler installed successfully\n");
-	}
+	if (ACPI_FAILURE(status))
+		printk(KERN_ERR "  Error installing notify handler\n");
 
-	/* For HOTK laptops: init the hotk->brightness value */
+	/* For laptops without GPLV: init the hotk->brightness value */
 	if ((!hotk->methods->brightness_get) && (!hotk->methods->brightness_status) &&
 	    (hotk->methods->brightness_up && hotk->methods->brightness_down)) {
 		status = acpi_evaluate_object(NULL, hotk->methods->brightness_down,
 					      NULL, NULL);
 		if (ACPI_FAILURE(status))
-			printk(KERN_NOTICE "  Error changing brightness\n");
-		status = acpi_evaluate_object(NULL, hotk->methods->brightness_up,
-					      NULL, NULL);
-		if (ACPI_FAILURE(status))
-			printk(KERN_NOTICE "  Error changing brightness\n");
+			printk(KERN_WARNING "  Error changing brightness\n");
+		else {
+			status = acpi_evaluate_object(NULL, hotk->methods->brightness_up,
+						      NULL, NULL);
+			if (ACPI_FAILURE(status))
+				printk(KERN_WARNING "  Strange, error changing" 
+				       " brightness\n");
+		}
 	}
 
       end:
@@ -888,7 +936,7 @@
 	status = acpi_remove_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY,
 					    asus_hotk_notify);
 	if (ACPI_FAILURE(status))
-		printk(KERN_NOTICE "Error removing notify handler\n");
+		printk(KERN_ERR "Asus ACPI: Error removing notify handler\n");
 
 	kfree(hotk);
 
@@ -900,35 +948,17 @@
 
 static int __init asus_acpi_init(void)
 {
-	int result = 0;
-	acpi_status status = 0;
-	struct acpi_buffer dsdt = { ACPI_ALLOCATE_BUFFER, NULL };
-
-	printk(KERN_NOTICE "Asus Laptop ACPI Extras version %s\n",
-	       ASUS_ACPI_VERSION);
-	/*
-	 * Here is the code to know the model we are running on. We need to
-	 * know this before calling the acpi_bus_register_driver function, in
-	 * case the HID for the laptop we are running on is different from
-	 * ACPI_HOTK_HID, which I have never seen yet :)
-	 * 
-	 * This information is then available in the global var asus_info
-	 */
-	status = acpi_get_table(ACPI_TABLE_DSDT, 1, &dsdt);
-	if (ACPI_FAILURE(status)) {
-		printk(KERN_NOTICE "  Couldn't get the DSDT table header\n");
-	} else {
-		asus_info = (struct acpi_table_header *) dsdt.pointer;
-	}
+	int result;
 
 	asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir);
-	if (!asus_proc_dir)
+	if (!asus_proc_dir) {
+		printk(KERN_ERR "Asus ACPI: Unable to create /proc entry");
 		return(-ENODEV);
+	}
 	asus_proc_dir->owner = THIS_MODULE;
 
 	result = acpi_bus_register_driver(&asus_hotk_driver);
 	if (result < 0) {
-		printk(KERN_NOTICE "  Error registering " ACPI_HOTK_NAME " \n");
 		remove_proc_entry(PROC_ASUS, acpi_root_dir);
 		return(-ENODEV);
 	}
diff -urN linux-2.4.23-pre5/drivers/acpi/bus.c linux-2.4.23-pre6/drivers/acpi/bus.c
--- linux-2.4.23-pre5/drivers/acpi/bus.c	2003-08-25 04:44:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/acpi/bus.c	2003-10-01 10:38:45.000000000 -0700
@@ -1898,9 +1898,7 @@
 	 * of that.
 	 */
 	result = acpi_ec_ecdt_probe();
-	if (result) {
-		goto error1;
-	}
+	/* Ignore result. Not having an ECDT is not fatal. */
 #endif
 
 	status = acpi_initialize_objects(ACPI_FULL_INITIALIZATION);
diff -urN linux-2.4.23-pre5/drivers/acpi/dispatcher/dsfield.c linux-2.4.23-pre6/drivers/acpi/dispatcher/dsfield.c
--- linux-2.4.23-pre5/drivers/acpi/dispatcher/dsfield.c	2003-08-25 04:44:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/acpi/dispatcher/dsfield.c	2003-10-01 10:38:45.000000000 -0700
@@ -105,27 +105,33 @@
 		return_ACPI_STATUS (AE_AML_NO_OPERAND);
 	}
 
-	/*
-	 * During the load phase, we want to enter the name of the field into
-	 * the namespace.  During the execute phase (when we evaluate the size
-	 * operand), we want to lookup the name
-	 */
-	if (walk_state->parse_flags & ACPI_PARSE_EXECUTE) {
-		flags = ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE;
+	if (walk_state->deferred_node) {
+		node = walk_state->deferred_node;
+		status = AE_OK;
 	}
 	else {
-		flags = ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE | ACPI_NS_ERROR_IF_FOUND;
-	}
+		/*
+		 * During the load phase, we want to enter the name of the field into
+		 * the namespace.  During the execute phase (when we evaluate the size
+		 * operand), we want to lookup the name
+		 */
+		if (walk_state->parse_flags & ACPI_PARSE_EXECUTE) {
+			flags = ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE;
+		}
+		else {
+			flags = ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE | ACPI_NS_ERROR_IF_FOUND;
+		}
 
-	/*
-	 * Enter the name_string into the namespace
-	 */
-	status = acpi_ns_lookup (walk_state->scope_info, arg->common.value.string,
-			 ACPI_TYPE_ANY, ACPI_IMODE_LOAD_PASS1,
-			 flags, walk_state, &(node));
-	if (ACPI_FAILURE (status)) {
-		ACPI_REPORT_NSERROR (arg->common.value.string, status);
-		return_ACPI_STATUS (status);
+		/*
+		 * Enter the name_string into the namespace
+		 */
+		status = acpi_ns_lookup (walk_state->scope_info, arg->common.value.string,
+				 ACPI_TYPE_ANY, ACPI_IMODE_LOAD_PASS1,
+				 flags, walk_state, &(node));
+		if (ACPI_FAILURE (status)) {
+			ACPI_REPORT_NSERROR (arg->common.value.string, status);
+			return_ACPI_STATUS (status);
+		}
 	}
 
 	/* We could put the returned object (Node) on the object stack for later, but
diff -urN linux-2.4.23-pre5/drivers/acpi/dispatcher/dsinit.c linux-2.4.23-pre6/drivers/acpi/dispatcher/dsinit.c
--- linux-2.4.23-pre5/drivers/acpi/dispatcher/dsinit.c	2003-08-25 04:44:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/acpi/dispatcher/dsinit.c	2003-10-01 10:38:45.000000000 -0700
@@ -135,7 +135,7 @@
 		}
 
 		/*
-		 * Always parse methods to detect errors, we may delete
+		 * Always parse methods to detect errors, we will delete
 		 * the parse tree below
 		 */
 		status = acpi_ds_parse_method (obj_handle);
@@ -150,7 +150,7 @@
 		}
 
 		/*
-		 * Delete the parse tree.  We simple re-parse the method
+		 * Delete the parse tree.  We simply re-parse the method
 		 * for every execution since there isn't much overhead
 		 */
 		acpi_ns_delete_namespace_subtree (obj_handle);
diff -urN linux-2.4.23-pre5/drivers/acpi/dispatcher/dsopcode.c linux-2.4.23-pre6/drivers/acpi/dispatcher/dsopcode.c
--- linux-2.4.23-pre5/drivers/acpi/dispatcher/dsopcode.c	2003-08-25 04:44:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/acpi/dispatcher/dsopcode.c	2003-10-01 10:38:45.000000000 -0700
@@ -65,7 +65,7 @@
  *
  * RETURN:      Status.
  *
- * DESCRIPTION: Late execution of region or field arguments
+ * DESCRIPTION: Late (deferred) execution of region or field arguments
  *
  ****************************************************************************/
 
@@ -111,7 +111,10 @@
 		return_ACPI_STATUS (status);
 	}
 
+	/* Mark this parse as a deferred opcode */
+
 	walk_state->parse_flags = ACPI_PARSE_DEFERRED_OP;
+	walk_state->deferred_node = node;
 
 	/* Pass1: Parse the entire declaration */
 
@@ -128,7 +131,7 @@
 	arg->common.node = node;
 	acpi_ps_delete_parse_tree (op);
 
-	/* Evaluate the address and length arguments for the Buffer Field */
+	/* Evaluate the deferred arguments */
 
 	op = acpi_ps_alloc_op (AML_INT_EVAL_SUBTREE_OP);
 	if (!op) {
@@ -144,6 +147,8 @@
 		return_ACPI_STATUS (AE_NO_MEMORY);
 	}
 
+	/* Execute the opcode and arguments */
+
 	status = acpi_ds_init_aml_walk (walk_state, op, NULL, aml_start,
 			  aml_length, NULL, NULL, 3);
 	if (ACPI_FAILURE (status)) {
@@ -151,6 +156,9 @@
 		return_ACPI_STATUS (status);
 	}
 
+	/* Mark this execution as a deferred opcode */
+
+	walk_state->deferred_node = node;
 	status = acpi_ps_parse_aml (walk_state);
 	acpi_ps_delete_parse_tree (op);
 	return_ACPI_STATUS (status);
@@ -192,7 +200,7 @@
 	node = obj_desc->buffer_field.node;
 
 	ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname (ACPI_TYPE_BUFFER_FIELD, node, NULL));
-	ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[%4.4s] buffer_field JIT Init\n",
+	ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[%4.4s] buffer_field Arg Init\n",
 		node->name.ascii));
 
 	/* Execute the AML code for the term_arg arguments */
@@ -207,7 +215,7 @@
  *
  * FUNCTION:    acpi_ds_get_buffer_arguments
  *
- * PARAMETERS:  obj_desc        - A valid Bufferobject
+ * PARAMETERS:  obj_desc        - A valid Buffer object
  *
  * RETURN:      Status.
  *
@@ -240,7 +248,7 @@
 		return_ACPI_STATUS (AE_AML_INTERNAL);
 	}
 
-	ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Buffer JIT Init\n"));
+	ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Buffer Arg Init\n"));
 
 	/* Execute the AML code for the term_arg arguments */
 
@@ -254,7 +262,7 @@
  *
  * FUNCTION:    acpi_ds_get_package_arguments
  *
- * PARAMETERS:  obj_desc        - A valid Packageobject
+ * PARAMETERS:  obj_desc        - A valid Package object
  *
  * RETURN:      Status.
  *
@@ -287,7 +295,7 @@
 		return_ACPI_STATUS (AE_AML_INTERNAL);
 	}
 
-	ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Package JIT Init\n"));
+	ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Package Arg Init\n"));
 
 	/* Execute the AML code for the term_arg arguments */
 
@@ -335,11 +343,12 @@
 
 	node = obj_desc->region.node;
 
-	ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname (ACPI_TYPE_REGION, node, NULL));
+	ACPI_DEBUG_EXEC (acpi_ut_display_init_pathname (ACPI_TYPE_REGION, node, NULL));
 
-	ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[%4.4s] op_region Init at AML %p\n",
+	ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[%4.4s] op_region Arg Init at AML %p\n",
 		node->name.ascii, extra_desc->extra.aml_start));
 
+	/* Execute the argument AML */
 
 	status = acpi_ds_execute_arguments (node, acpi_ns_get_parent_node (node),
 			 extra_desc->extra.aml_length, extra_desc->extra.aml_start);
@@ -505,14 +514,16 @@
 		goto cleanup;
 	}
 
-
 	/* Entire field must fit within the current length of the buffer */
 
 	if ((bit_offset + bit_count) >
 		(8 * (u32) buffer_desc->buffer.length)) {
 		ACPI_DEBUG_PRINT ((ACPI_DB_ERROR,
-			"Field size %d exceeds Buffer size %d (bits)\n",
-			 bit_offset + bit_count, 8 * (u32) buffer_desc->buffer.length));
+			"Field [%4.4s] size %d exceeds Buffer [%4.4s] size %d (bits)\n",
+			((struct acpi_namespace_node *) result_desc)->name.ascii,
+			 bit_offset + bit_count,
+			 buffer_desc->buffer.node->name.ascii,
+			 8 * (u32) buffer_desc->buffer.length));
 		status = AE_AML_BUFFER_LIMIT;
 		goto cleanup;
 	}
diff -urN linux-2.4.23-pre5/drivers/acpi/dispatcher/dsutils.c linux-2.4.23-pre6/drivers/acpi/dispatcher/dsutils.c
--- linux-2.4.23-pre5/drivers/acpi/dispatcher/dsutils.c	2003-08-25 04:44:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/acpi/dispatcher/dsutils.c	2003-10-01 10:38:45.000000000 -0700
@@ -53,6 +53,7 @@
 #define _COMPONENT          ACPI_DISPATCHER
 	 ACPI_MODULE_NAME    ("dsutils")
 
+
 #ifndef ACPI_NO_METHOD_EXECUTION
 
 /*******************************************************************************
@@ -196,7 +197,6 @@
 			acpi_ps_get_opcode_name (op->common.parent->common.aml_opcode), op));
 
 	return_VALUE (FALSE);
-
 }
 
 
@@ -239,7 +239,6 @@
 		return_VOID;
 	}
 
-
 	if (!acpi_ds_is_result_used (op, walk_state)) {
 		/*
 		 * Must pop the result stack (obj_desc should be equal to result_obj)
@@ -389,61 +388,77 @@
 		 * in name_string
 		 */
 
+
 		/*
-		 * Differentiate between a namespace "create" operation
-		 * versus a "lookup" operation (IMODE_LOAD_PASS2 vs.
-		 * IMODE_EXECUTE) in order to support the creation of
-		 * namespace objects during the execution of control methods.
+		 * Special handling for buffer_field declarations. This is a deferred
+		 * opcode that unfortunately defines the field name as the last
+		 * parameter instead of the first.  We get here when we are performing
+		 * the deferred execution, so the actual name of the field is already
+		 * in the namespace.  We don't want to attempt to look it up again
+		 * because we may be executing in a different scope than where the
+		 * actual opcode exists.
 		 */
-		parent_op = arg->common.parent;
-		op_info = acpi_ps_get_opcode_info (parent_op->common.aml_opcode);
-		if ((op_info->flags & AML_NSNODE) &&
-			(parent_op->common.aml_opcode != AML_INT_METHODCALL_OP) &&
-			(parent_op->common.aml_opcode != AML_REGION_OP) &&
-			(parent_op->common.aml_opcode != AML_INT_NAMEPATH_OP)) {
-			/* Enter name into namespace if not found */
-
-			interpreter_mode = ACPI_IMODE_LOAD_PASS2;
+		if ((walk_state->deferred_node) &&
+			(walk_state->deferred_node->type == ACPI_TYPE_BUFFER_FIELD) &&
+			(arg_index != 0)) {
+			obj_desc = ACPI_CAST_PTR (union acpi_operand_object, walk_state->deferred_node);
+			status = AE_OK;
 		}
+		else    /* All other opcodes */ {
+			/*
+			 * Differentiate between a namespace "create" operation
+			 * versus a "lookup" operation (IMODE_LOAD_PASS2 vs.
+			 * IMODE_EXECUTE) in order to support the creation of
+			 * namespace objects during the execution of control methods.
+			 */
+			parent_op = arg->common.parent;
+			op_info = acpi_ps_get_opcode_info (parent_op->common.aml_opcode);
+			if ((op_info->flags & AML_NSNODE) &&
+				(parent_op->common.aml_opcode != AML_INT_METHODCALL_OP) &&
+				(parent_op->common.aml_opcode != AML_REGION_OP) &&
+				(parent_op->common.aml_opcode != AML_INT_NAMEPATH_OP)) {
+				/* Enter name into namespace if not found */
 
-		else {
-			/* Return a failure if name not found */
-
-			interpreter_mode = ACPI_IMODE_EXECUTE;
-		}
+				interpreter_mode = ACPI_IMODE_LOAD_PASS2;
+			}
+			else {
+				/* Return a failure if name not found */
 
-		status = acpi_ns_lookup (walk_state->scope_info, name_string,
-				 ACPI_TYPE_ANY, interpreter_mode,
-				 ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE,
-				 walk_state,
-				 ACPI_CAST_INDIRECT_PTR (struct acpi_namespace_node, &obj_desc));
-		/*
-		 * The only case where we pass through (ignore) a NOT_FOUND
-		 * error is for the cond_ref_of opcode.
-		 */
-		if (status == AE_NOT_FOUND) {
-			if (parent_op->common.aml_opcode == AML_COND_REF_OF_OP) {
-				/*
-				 * For the Conditional Reference op, it's OK if
-				 * the name is not found;  We just need a way to
-				 * indicate this to the interpreter, set the
-				 * object to the root
-				 */
-				obj_desc = ACPI_CAST_PTR (union acpi_operand_object, acpi_gbl_root_node);
-				status = AE_OK;
+				interpreter_mode = ACPI_IMODE_EXECUTE;
 			}
 
-			else {
-				/*
-				 * We just plain didn't find it -- which is a
-				 * very serious error at this point
-				 */
-				status = AE_AML_NAME_NOT_FOUND;
+			status = acpi_ns_lookup (walk_state->scope_info, name_string,
+					 ACPI_TYPE_ANY, interpreter_mode,
+					 ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE,
+					 walk_state,
+					 ACPI_CAST_INDIRECT_PTR (struct acpi_namespace_node, &obj_desc));
+			/*
+			 * The only case where we pass through (ignore) a NOT_FOUND
+			 * error is for the cond_ref_of opcode.
+			 */
+			if (status == AE_NOT_FOUND) {
+				if (parent_op->common.aml_opcode == AML_COND_REF_OF_OP) {
+					/*
+					 * For the Conditional Reference op, it's OK if
+					 * the name is not found;  We just need a way to
+					 * indicate this to the interpreter, set the
+					 * object to the root
+					 */
+					obj_desc = ACPI_CAST_PTR (union acpi_operand_object, acpi_gbl_root_node);
+					status = AE_OK;
+				}
+				else {
+					/*
+					 * We just plain didn't find it -- which is a
+					 * very serious error at this point
+					 */
+					status = AE_AML_NAME_NOT_FOUND;
+				}
 			}
-		}
 
-		if (ACPI_FAILURE (status)) {
-			ACPI_REPORT_NSERROR (name_string, status);
+			if (ACPI_FAILURE (status)) {
+				ACPI_REPORT_NSERROR (name_string, status);
+			}
 		}
 
 		/* Free the namestring created above */
@@ -464,8 +479,6 @@
 		}
 		ACPI_DEBUGGER_EXEC (acpi_db_display_argument_object (obj_desc, walk_state));
 	}
-
-
 	else {
 		/* Check for null name case */
 
@@ -480,7 +493,6 @@
 
 			ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Null namepath: Arg=%p\n", arg));
 		}
-
 		else {
 			opcode = arg->common.aml_opcode;
 		}
diff -urN linux-2.4.23-pre5/drivers/acpi/dispatcher/dswload.c linux-2.4.23-pre6/drivers/acpi/dispatcher/dswload.c
--- linux-2.4.23-pre5/drivers/acpi/dispatcher/dswload.c	2003-08-25 04:44:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/acpi/dispatcher/dswload.c	2003-10-01 10:38:45.000000000 -0700
@@ -248,6 +248,14 @@
 		 *       buffer_field, or Package), the name of the object is already
 		 *       in the namespace.
 		 */
+		if (walk_state->deferred_node) {
+			/* This name is already in the namespace, get the node */
+
+			node = walk_state->deferred_node;
+			status = AE_OK;
+			break;
+		}
+
 		flags = ACPI_NS_NO_UPSEARCH;
 		if ((walk_state->opcode != AML_SCOPE_OP) &&
 			(!(walk_state->parse_flags & ACPI_PARSE_DEFERRED_OP))) {
@@ -589,7 +597,17 @@
 		 * Enter the named type into the internal namespace.  We enter the name
 		 * as we go downward in the parse tree.  Any necessary subobjects that involve
 		 * arguments to the opcode must be created as we go back up the parse tree later.
+		 *
+		 * Note: Name may already exist if we are executing a deferred opcode.
 		 */
+		if (walk_state->deferred_node) {
+			/* This name is already in the namespace, get the node */
+
+			node = walk_state->deferred_node;
+			status = AE_OK;
+			break;
+		}
+
 		status = acpi_ns_lookup (walk_state->scope_info, buffer_ptr, object_type,
 				  ACPI_IMODE_EXECUTE, ACPI_NS_NO_UPSEARCH, walk_state, &(node));
 		break;
diff -urN linux-2.4.23-pre5/drivers/acpi/dispatcher/dswscope.c linux-2.4.23-pre6/drivers/acpi/dispatcher/dswscope.c
--- linux-2.4.23-pre5/drivers/acpi/dispatcher/dswscope.c	2003-08-25 04:44:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/acpi/dispatcher/dswscope.c	2003-10-01 10:38:45.000000000 -0700
@@ -121,10 +121,9 @@
 	/* Make sure object type is valid */
 
 	if (!acpi_ut_valid_object_type (type)) {
-		ACPI_REPORT_WARNING (("ds_scope_stack_push: type code out of range\n"));
+		ACPI_REPORT_WARNING (("ds_scope_stack_push: Invalid object type: 0x%X\n", type));
 	}
 
-
 	/* Allocate a new scope object */
 
 	scope_info = acpi_ut_create_generic_state ();
@@ -146,13 +145,13 @@
 	old_scope_info = walk_state->scope_info;
 	if (old_scope_info) {
 		ACPI_DEBUG_PRINT_RAW ((ACPI_DB_EXEC,
-			"[%4.4s] (%10s)",
+			"[%4.4s] (%s)",
 			old_scope_info->scope.node->name.ascii,
 			acpi_ut_get_type_name (old_scope_info->common.value)));
 	}
 	else {
 		ACPI_DEBUG_PRINT_RAW ((ACPI_DB_EXEC,
-			"[\\___] (%10s)", "ROOT"));
+			"[\\___] (%s)", "ROOT"));
 	}
 
 	ACPI_DEBUG_PRINT_RAW ((ACPI_DB_EXEC,
@@ -163,7 +162,6 @@
 	/* Push new scope object onto stack */
 
 	acpi_ut_push_generic_state (&walk_state->scope_info, scope_info);
-
 	return_ACPI_STATUS (AE_OK);
 }
 
@@ -207,7 +205,7 @@
 	walk_state->scope_depth--;
 
 	ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
-		"[%.2d] Popped scope [%4.4s] (%10s), New scope -> ",
+		"[%.2d] Popped scope [%4.4s] (%s), New scope -> ",
 		(u32) walk_state->scope_depth,
 		scope_info->scope.node->name.ascii,
 		acpi_ut_get_type_name (scope_info->common.value)));
@@ -225,7 +223,6 @@
 	}
 
 	acpi_ut_delete_generic_state (scope_info);
-
 	return_ACPI_STATUS (AE_OK);
 }
 
diff -urN linux-2.4.23-pre5/drivers/acpi/dispatcher/dswstate.c linux-2.4.23-pre6/drivers/acpi/dispatcher/dswstate.c
--- linux-2.4.23-pre5/drivers/acpi/dispatcher/dswstate.c	2003-08-25 04:44:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/acpi/dispatcher/dswstate.c	2003-10-01 10:38:45.000000000 -0700
@@ -56,11 +56,12 @@
  * FUNCTION:    acpi_ds_result_insert
  *
  * PARAMETERS:  Object              - Object to push
+ *              Index               - Where to insert the object
  *              walk_state          - Current Walk state
  *
  * RETURN:      Status
  *
- * DESCRIPTION: Push an object onto this walk's result stack
+ * DESCRIPTION: Insert an object onto this walk's result stack
  *
  ******************************************************************************/
 
@@ -114,6 +115,7 @@
  * FUNCTION:    acpi_ds_result_remove
  *
  * PARAMETERS:  Object              - Where to return the popped object
+ *              Index               - Where to extract the object
  *              walk_state          - Current Walk state
  *
  * RETURN:      Status
@@ -233,6 +235,7 @@
 	return (AE_AML_NO_RETURN_VALUE);
 }
 
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_ds_result_pop_from_bottom
@@ -295,7 +298,6 @@
 		*object, (*object) ? acpi_ut_get_object_type_name (*object) : "NULL",
 		state, walk_state));
 
-
 	return (AE_OK);
 }
 
@@ -358,8 +360,7 @@
  *
  * FUNCTION:    acpi_ds_result_stack_push
  *
- * PARAMETERS:  Object              - Object to push
- *              walk_state          - Current Walk state
+ * PARAMETERS:  walk_state          - Current Walk state
  *
  * RETURN:      Status
  *
@@ -420,7 +421,6 @@
 		return (AE_AML_NO_OPERAND);
 	}
 
-
 	state = acpi_ut_pop_generic_state (&walk_state->results);
 
 	ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
@@ -572,6 +572,7 @@
 }
 #endif
 
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_ds_obj_stack_pop
@@ -641,6 +642,7 @@
 	u32                             i;
 	union acpi_operand_object       *obj_desc;
 
+
 	ACPI_FUNCTION_NAME ("ds_obj_stack_pop_and_delete");
 
 
@@ -883,8 +885,15 @@
  * FUNCTION:    acpi_ds_init_aml_walk
  *
  * PARAMETERS:  walk_state      - New state to be initialized
+ *              Op              - Current parse op
+ *              method_node     - Control method NS node, if any
+ *              aml_start       - Start of AML
+ *              aml_length      - Length of AML
+ *              Params          - Method args, if any
+ *              return_obj_desc - Where to store a return object, if any
+ *              pass_number     - 1, 2, or 3
  *
- * RETURN:      None
+ * RETURN:      Status
  *
  * DESCRIPTION: Initialize a walk state for a pass 1 or 2 parse tree walk
  *
@@ -927,9 +936,9 @@
 
 	if (method_node) {
 		walk_state->parser_state.start_node = method_node;
-		walk_state->walk_type               = ACPI_WALK_METHOD;
-		walk_state->method_node             = method_node;
-		walk_state->method_desc             = acpi_ns_get_attached_object (method_node);
+		walk_state->walk_type            = ACPI_WALK_METHOD;
+		walk_state->method_node          = method_node;
+		walk_state->method_desc          = acpi_ns_get_attached_object (method_node);
 
 		/* Push start scope on scope stack and make it current  */
 
@@ -956,6 +965,7 @@
 		while (extra_op && !extra_op->common.node) {
 			extra_op = extra_op->common.parent;
 		}
+
 		if (!extra_op) {
 			parser_state->start_node = NULL;
 		}
@@ -1014,7 +1024,7 @@
 		ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "%p walk still has a scope list\n", walk_state));
 	}
 
-   /* Always must free any linked control states */
+	/* Always must free any linked control states */
 
 	while (walk_state->control_state) {
 		state = walk_state->control_state;
diff -urN linux-2.4.23-pre5/drivers/acpi/executer/excreate.c linux-2.4.23-pre6/drivers/acpi/executer/excreate.c
--- linux-2.4.23-pre5/drivers/acpi/executer/excreate.c	2003-08-25 04:44:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/acpi/executer/excreate.c	2003-10-01 10:38:45.000000000 -0700
@@ -286,7 +286,7 @@
 	ACPI_FUNCTION_TRACE ("ex_create_region");
 
 
-	/* Get the Node from the object stack  */
+	/* Get the Namespace Node */
 
 	node = walk_state->op->common.node;
 
@@ -311,7 +311,6 @@
 	ACPI_DEBUG_PRINT ((ACPI_DB_LOAD, "Region Type - %s (%X)\n",
 			  acpi_ut_get_region_name (region_space), region_space));
 
-
 	/* Create the region descriptor */
 
 	obj_desc = acpi_ut_create_internal_object (ACPI_TYPE_REGION);
@@ -375,6 +374,7 @@
 
 	ACPI_FUNCTION_TRACE ("ex_create_table_region");
 
+
 	/* Get the Node from the object stack  */
 
 	node = walk_state->op->common.node;
@@ -392,7 +392,6 @@
 	status = acpi_tb_find_table (operand[1]->string.pointer,
 			   operand[2]->string.pointer,
 			   operand[3]->string.pointer, &table);
-
 	if (ACPI_FAILURE (status)) {
 		return_ACPI_STATUS (status);
 	}
@@ -489,7 +488,6 @@
 	status = acpi_ns_attach_object ((struct acpi_namespace_node *) operand[0],
 			  obj_desc, ACPI_TYPE_PROCESSOR);
 
-
 	/* Remove local reference to the object */
 
 	acpi_ut_remove_reference (obj_desc);
@@ -540,7 +538,6 @@
 	status = acpi_ns_attach_object ((struct acpi_namespace_node *) operand[0],
 			  obj_desc, ACPI_TYPE_POWER);
 
-
 	/* Remove local reference to the object */
 
 	acpi_ut_remove_reference (obj_desc);
@@ -609,7 +606,6 @@
 		obj_desc->method.concurrency = (u8)
 				  (((method_flags & METHOD_FLAGS_SYNCH_LEVEL) >> 4) + 1);
 	}
-
 	else {
 		obj_desc->method.concurrency = INFINITE_CONCURRENCY;
 	}
diff -urN linux-2.4.23-pre5/drivers/acpi/namespace/nsdump.c linux-2.4.23-pre6/drivers/acpi/namespace/nsdump.c
--- linux-2.4.23-pre5/drivers/acpi/namespace/nsdump.c	2003-08-25 04:44:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/acpi/namespace/nsdump.c	2003-10-01 10:38:45.000000000 -0700
@@ -234,7 +234,7 @@
 
 		case ACPI_TYPE_DEVICE:
 
-			acpi_os_printf ("Notify object: %p", obj_desc);
+			acpi_os_printf ("Notify Object: %p\n", obj_desc);
 			break;
 
 
@@ -371,7 +371,7 @@
 		case ACPI_TYPE_LOCAL_BANK_FIELD:
 		case ACPI_TYPE_LOCAL_INDEX_FIELD:
 
-			acpi_os_printf (" Off %.2X Len %.2X Acc %.2hd\n",
+			acpi_os_printf ("Off %.2X Len %.2X Acc %.2hd\n",
 					(obj_desc->common_field.base_byte_offset * 8)
 						+ obj_desc->common_field.start_field_bit_offset,
 					obj_desc->common_field.bit_length,
diff -urN linux-2.4.23-pre5/drivers/acpi/namespace/nssearch.c linux-2.4.23-pre6/drivers/acpi/namespace/nssearch.c
--- linux-2.4.23-pre5/drivers/acpi/namespace/nssearch.c	2003-08-25 04:44:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/acpi/namespace/nssearch.c	2003-10-01 10:38:45.000000000 -0700
@@ -96,7 +96,7 @@
 
 		scope_name = acpi_ns_get_external_pathname (node);
 		if (scope_name) {
-			ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "Searching %s [%p] For %4.4s (%s)\n",
+			ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "Searching %s (%p) For [%4.4s] (%s)\n",
 				scope_name, node, (char *) &target_name, acpi_ut_get_type_name (type)));
 
 			ACPI_MEM_FREE (scope_name);
@@ -117,9 +117,9 @@
 			 * Found matching entry.
 			 */
 			ACPI_DEBUG_PRINT ((ACPI_DB_NAMES,
-				"Name %4.4s Type [%s] found in scope [%4.4s] %p\n",
+				"Name [%4.4s] (%s) %p found in scope [%4.4s] %p\n",
 				(char *) &target_name, acpi_ut_get_type_name (next_node->type),
-				next_node->name.ascii, next_node));
+				next_node, node->name.ascii, node));
 
 			*return_node = next_node;
 			return_ACPI_STATUS (AE_OK);
@@ -143,7 +143,7 @@
 	/* Searched entire namespace level, not found */
 
 	ACPI_DEBUG_PRINT ((ACPI_DB_NAMES,
-		"Name %4.4s Type [%s] not found in search in scope [%4.4s] %p first child %p\n",
+		"Name [%4.4s] (%s) not found in search in scope [%4.4s] %p first child %p\n",
 		(char *) &target_name, acpi_ut_get_type_name (type),
 		node->name.ascii, node, node->child));
 
diff -urN linux-2.4.23-pre5/drivers/acpi/namespace/nsutils.c linux-2.4.23-pre6/drivers/acpi/namespace/nsutils.c
--- linux-2.4.23-pre5/drivers/acpi/namespace/nsutils.c	2003-08-25 04:44:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/acpi/namespace/nsutils.c	2003-10-01 10:38:45.000000000 -0700
@@ -175,6 +175,11 @@
 	acpi_status                     status;
 
 
+	if (!node) {
+		acpi_os_printf ("[NULL NAME]");
+		return;
+	}
+
 	/* Convert handle to a full pathname and print it (with supplied message) */
 
 	buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER;
@@ -470,11 +475,11 @@
 	*result = 0;
 
 	if (info->fully_qualified) {
-		ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "returning [%p] (abs) \"\\%s\"\n",
+		ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Returning [%p] (abs) \"\\%s\"\n",
 			internal_name, internal_name));
 	}
 	else {
-		ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "returning [%p] (rel) \"%s\"\n",
+		ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Returning [%p] (rel) \"%s\"\n",
 			internal_name, internal_name));
 	}
 
diff -urN linux-2.4.23-pre5/drivers/acpi/parser/psparse.c linux-2.4.23-pre6/drivers/acpi/parser/psparse.c
--- linux-2.4.23-pre5/drivers/acpi/parser/psparse.c	2003-08-25 04:44:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/acpi/parser/psparse.c	2003-10-01 10:38:45.000000000 -0700
@@ -437,7 +437,6 @@
 		return_ACPI_STATUS (AE_BAD_PARAMETER);
 	}
 
-
 	parser_state = &walk_state->parser_state;
 	walk_state->arg_types = 0;
 
@@ -705,10 +704,9 @@
 				walk_state->arg_types = 0;
 				break;
 
-
 			default:
 
-				/* Op is not a constant or string, append each argument */
+				/* Op is not a constant or string, append each argument to the Op */
 
 				while (GET_CURRENT_ARG_TYPE (walk_state->arg_types) &&
 						!walk_state->arg_count) {
@@ -727,23 +725,23 @@
 					INCREMENT_ARG_LIST (walk_state->arg_types);
 				}
 
+				/* Special processing for certain opcodes */
+
 				switch (op->common.aml_opcode) {
 				case AML_METHOD_OP:
 
-					/* For a method, save the length and address of the body */
-
 					/*
-					 * Skip parsing of control method or opregion body,
+					 * Skip parsing of control method
 					 * because we don't have enough info in the first pass
-					 * to parse them correctly.
+					 * to parse it correctly.
+					 *
+					 * Save the length and address of the body
 					 */
 					op->named.data   = parser_state->aml;
 					op->named.length = (u32) (parser_state->pkg_end - parser_state->aml);
-					/*
-					 * Skip body of method.  For op_regions, we must continue
-					 * parsing because the opregion is not a standalone
-					 * package (We don't know where the end is).
-					 */
+
+					/* Skip body of method */
+
 					parser_state->aml   = parser_state->pkg_end;
 					walk_state->arg_count = 0;
 					break;
@@ -756,15 +754,15 @@
 						(op->common.parent->common.aml_opcode == AML_NAME_OP) &&
 						(walk_state->descending_callback != acpi_ds_exec_begin_op)) {
 						/*
-						 * Skip parsing of
+						 * Skip parsing of Buffers and Packages
 						 * because we don't have enough info in the first pass
 						 * to parse them correctly.
 						 */
 						op->named.data   = aml_op_start;
 						op->named.length = (u32) (parser_state->pkg_end - aml_op_start);
-						/*
-						 * Skip body
-						 */
+
+						/* Skip body */
+
 						parser_state->aml   = parser_state->pkg_end;
 						walk_state->arg_count = 0;
 					}
@@ -778,6 +776,7 @@
 					break;
 
 				default:
+
 					/* No action for all other opcodes */
 					break;
 				}
diff -urN linux-2.4.23-pre5/drivers/acpi/pci_irq.c linux-2.4.23-pre6/drivers/acpi/pci_irq.c
--- linux-2.4.23-pre5/drivers/acpi/pci_irq.c	2003-08-25 04:44:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/acpi/pci_irq.c	2003-10-01 10:38:45.000000000 -0700
@@ -73,6 +73,9 @@
 
 	ACPI_FUNCTION_TRACE("acpi_pci_irq_find_prt_entry");
 
+	if (!acpi_prt.count)
+		return_PTR(NULL);
+
 	/*
 	 * Parse through all PRT entries looking for a match on the specified
 	 * PCI device's segment, bus, device, and pin (don't care about func).
diff -urN linux-2.4.23-pre5/drivers/acpi/pci_link.c linux-2.4.23-pre6/drivers/acpi/pci_link.c
--- linux-2.4.23-pre5/drivers/acpi/pci_link.c	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/acpi/pci_link.c	2003-10-01 10:38:45.000000000 -0700
@@ -499,16 +499,16 @@
 		irq = link->irq.active;
 	} else {
 		irq = link->irq.possible[0];
-	}
 
-		/* 
-		 * Select the best IRQ.  This is done in reverse to promote 
+		/*
+		 * Select the best IRQ.  This is done in reverse to promote
 		 * the use of IRQs 9, 10, 11, and >15.
 		 */
 		for (i=(link->irq.possible_count-1); i>0; i--) {
 			if (acpi_irq_penalty[irq] > acpi_irq_penalty[link->irq.possible[i]])
 				irq = link->irq.possible[i];
 		}
+	}
 
 	/* Attempt to enable the link device at this IRQ. */
 	if (acpi_pci_link_set(link, irq)) {
diff -urN linux-2.4.23-pre5/drivers/atm/he.c linux-2.4.23-pre6/drivers/atm/he.c
--- linux-2.4.23-pre5/drivers/atm/he.c	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/atm/he.c	2003-10-01 10:38:45.000000000 -0700
@@ -2866,8 +2866,10 @@
 			if (!capable(CAP_NET_ADMIN))
 				return -EPERM;
 
-			copy_from_user(&reg, (struct he_ioctl_reg *) arg,
-						sizeof(struct he_ioctl_reg));
+			if (copy_from_user(&reg, (struct he_ioctl_reg *) arg,
+						sizeof(struct he_ioctl_reg)))
+				return -EFAULT;
+			
 			spin_lock_irqsave(&he_dev->global_lock, flags);
 			switch (reg.type) {
 				case HE_REGTYPE_PCI:
@@ -2891,8 +2893,9 @@
 			}
 			spin_unlock_irqrestore(&he_dev->global_lock, flags);
 			if (err == 0)
-				copy_to_user((struct he_ioctl_reg *) arg, &reg,
-							sizeof(struct he_ioctl_reg));
+				if (copy_to_user((struct he_ioctl_reg *) arg, &reg,
+							sizeof(struct he_ioctl_reg)))
+					return -EFAULT;
 			break;
 		default:
 #ifdef CONFIG_ATM_HE_USE_SUNI
diff -urN linux-2.4.23-pre5/drivers/block/cciss.c linux-2.4.23-pre6/drivers/block/cciss.c
--- linux-2.4.23-pre5/drivers/block/cciss.c	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/block/cciss.c	2003-10-01 10:38:45.000000000 -0700
@@ -106,7 +106,15 @@
 
 #define READ_AHEAD 	 128
 #define NR_CMDS		 128 /* #commands that can be outstanding */
-#define MAX_CTLR 8
+#define MAX_CTLR	 32 
+
+/* No sense in giving up our preallocated major numbers */
+#if MAX_CTLR < 8
+#error"cciss.c: MAX_CTLR must be 8 or greater"
+#endif
+
+/* Originally cciss driver only supports 8 major number */
+#define MAX_CTLR_ORIG  COMPAQ_CISS_MAJOR7 - COMPAQ_CISS_MAJOR + 1
 
 #define CCISS_DMA_MASK 0xFFFFFFFFFFFFFFFF /* 64 bit DMA */
 
@@ -121,7 +129,7 @@
 #endif
 
 static ctlr_info_t *hba[MAX_CTLR];
-
+static int map_major_to_ctlr[MAX_BLKDEV] = {0}; /* gets ctlr num from maj num */
 static struct proc_dir_entry *proc_cciss;
 
 static void do_cciss_request(request_queue_t *q);
@@ -421,7 +429,7 @@
  */
 static int cciss_open(struct inode *inode, struct file *filep)
 {
-	int ctlr = MAJOR(inode->i_rdev) - MAJOR_NR;
+ 	int ctlr = map_major_to_ctlr[MAJOR(inode->i_rdev)];
 	int dsk  = MINOR(inode->i_rdev) >> NWD_SHIFT;
 
 #ifdef CCISS_DEBUG
@@ -462,7 +470,7 @@
  */
 static int cciss_release(struct inode *inode, struct file *filep)
 {
-	int ctlr = MAJOR(inode->i_rdev) - MAJOR_NR;
+	int ctlr = map_major_to_ctlr[MAJOR(inode->i_rdev)];
 	int dsk  = MINOR(inode->i_rdev) >> NWD_SHIFT;
 
 #ifdef CCISS_DEBUG
@@ -482,7 +490,7 @@
 static int cciss_ioctl(struct inode *inode, struct file *filep, 
 		unsigned int cmd, unsigned long arg)
 {
-	int ctlr = MAJOR(inode->i_rdev) - MAJOR_NR;
+	int ctlr = map_major_to_ctlr[MAJOR(inode->i_rdev)];
 	int dsk  = MINOR(inode->i_rdev) >> NWD_SHIFT;
 
 #ifdef CCISS_DEBUG
@@ -1028,7 +1036,7 @@
         int i;
 
         target = MINOR(dev) >> NWD_SHIFT;
-        ctlr = MAJOR(dev) - MAJOR_NR;
+	ctlr = map_major_to_ctlr[MAJOR(dev)];
         gdev = &(hba[ctlr]->gendisk);
 
         spin_lock_irqsave(&io_request_lock, flags);
@@ -1047,12 +1055,12 @@
 
         for(i=max_p-1; i>=0; i--) {
                 int minor = start+i;
-                invalidate_device(MKDEV(MAJOR_NR + ctlr, minor), 1);
+                invalidate_device(MKDEV(hba[ctlr]->major, minor), 1);
                 gdev->part[minor].start_sect = 0;
                 gdev->part[minor].nr_sects = 0;
 
                 /* reset the blocksize so we can read the partition table */
-                blksize_size[MAJOR_NR+ctlr][minor] = 1024;
+                blksize_size[hba[ctlr]->major][minor] = 1024;
         }
 	/* setup partitions per disk */
 	grok_partitions(gdev, target, MAX_PART, 
@@ -1093,7 +1101,7 @@
 	for (i=max_p-1; i>=0; i--) {
 		int minor = start+i;
 		/* printk("invalidating( %d %d)\n", ctlr, minor); */
-		invalidate_device(MKDEV(MAJOR_NR+ctlr, minor), 1);
+		invalidate_device(MKDEV(hba[ctlr]->major, minor), 1);
 		/* so open will now fail */
 		h->sizes[minor] = 0;
 		/* so it will no longer appear in /proc/partitions */
@@ -1581,12 +1589,12 @@
 
 	for(i=max_p-1; i>=0; i--) {
 		int minor = start+i;
-		invalidate_device(MKDEV(MAJOR_NR + ctlr, minor), 1);
+		invalidate_device(MKDEV(hba[ctlr]->major, minor), 1);
 		gdev->part[minor].start_sect = 0;
 		gdev->part[minor].nr_sects = 0;
 
 		/* reset the blocksize so we can read the partition table */
-		blksize_size[MAJOR_NR+ctlr][minor] = block_size;
+		blksize_size[hba[ctlr]->major][minor] = block_size;
 		hba[ctlr]->hardsizes[minor] = block_size;
 	}
 
@@ -1700,12 +1708,12 @@
 
 	for (i=max_p-1; i>=0; i--) {
 		int minor = start+i;
-		invalidate_device(MKDEV(MAJOR_NR + ctlr, minor), 1);
+		invalidate_device(MKDEV(hba[ctlr]->major, minor), 1);
 		gdev->part[minor].start_sect = 0;
 		gdev->part[minor].nr_sects = 0;
 
 		/* reset the blocksize so we can read the partition table */
-		blksize_size[MAJOR_NR+ctlr][minor] = 1024;
+		blksize_size[hba[ctlr]->major][minor] = block_size;
 		hba[ctlr]->hardsizes[minor] = block_size;
 	}
 
@@ -2269,7 +2277,7 @@
 	if (creq->nr_segments > MAXSGENTRIES)
                 BUG();
 
-        if (h->ctlr != MAJOR(creq->rq_dev)-MAJOR_NR ) {
+	if( h->ctlr != map_major_to_ctlr[MAJOR(creq->rq_dev)] ) {
                 printk(KERN_WARNING "doreq cmd for %d, %x at %p\n",
                                 h->ctlr, creq->rq_dev, creq);
                 blkdev_dequeue_request(creq);
@@ -2436,7 +2444,7 @@
 	/*
 	 * See if we can queue up some more IO
 	 */
-	do_cciss_request(BLK_DEFAULT_QUEUE(MAJOR_NR + h->ctlr));
+	do_cciss_request(BLK_DEFAULT_QUEUE(h->major));
 	spin_unlock_irqrestore(&io_request_lock, flags);
 }
 /* 
@@ -2874,8 +2882,10 @@
 			return i;
 		}
 	}
-	printk(KERN_WARNING "cciss: This driver supports a maximum"
-		" of 8 controllers.\n");
+	printk(KERN_WARNING 
+		"cciss: This driver supports a maximum of %d controllers.\n"
+		"You can change this value in cciss.c and recompile.\n",
+		MAX_CTLR);
 	return -1;
 }
 
@@ -3063,6 +3073,7 @@
 	request_queue_t *q;
 	int i;
 	int j;
+	int rc;
 
 	printk(KERN_DEBUG "cciss: Device 0x%x has been found at"
 			" bus %d dev %d func %d\n",
@@ -3078,16 +3089,33 @@
 	}
 	sprintf(hba[i]->devname, "cciss%d", i);
 	hba[i]->ctlr = i;
+
+	/* register with the major number, or get a dynamic major number */
+	/* by passing 0 as argument */
+
+	if (i < MAX_CTLR_ORIG)
+		hba[i]->major = MAJOR_NR + i;
+
 	hba[i]->pdev = pdev;
 	ASSERT_CTLR_ALIVE(hba[i]);
 
-	if (register_blkdev(MAJOR_NR+i, hba[i]->devname, &cciss_fops)) {
+	rc = (register_blkdev(hba[i]->major, hba[i]->devname, &cciss_fops));
+	if (rc < 0) {
 		printk(KERN_ERR "cciss:  Unable to get major number "
-			"%d for %s\n", MAJOR_NR+i, hba[i]->devname);
+			"%d for %s\n", hba[i]->major, hba[i]->devname);
 		release_io_mem(hba[i]);
 		free_hba(i);
 		return -1;
+	} else {
+		if (i < MAX_CTLR_ORIG) {
+			hba[i]->major = MAJOR_NR + i;
+			map_major_to_ctlr[MAJOR_NR + i] = i;
+		} else {
+			hba[i]->major = rc;
+			map_major_to_ctlr[rc] = i;
+		}
 	}
+
 	/* make sure the board interrupts are off */
 	hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_OFF);
 	if (request_irq(hba[i]->intr, do_cciss_intr, 
@@ -3096,7 +3124,8 @@
 
 		printk(KERN_ERR "cciss: Unable to get irq %d for %s\n",
 			hba[i]->intr, hba[i]->devname);
-		unregister_blkdev( MAJOR_NR+i, hba[i]->devname);
+		unregister_blkdev( hba[i]->major, hba[i]->devname);
+		map_major_to_ctlr[hba[i]->major] = 0;
 		release_io_mem(hba[i]);
 		free_hba(i);
 		return -1;
@@ -3125,7 +3154,8 @@
 				hba[i]->errinfo_pool, 
 				hba[i]->errinfo_pool_dhandle);
                 free_irq(hba[i]->intr, hba[i]);
-                unregister_blkdev(MAJOR_NR+i, hba[i]->devname);
+                unregister_blkdev(hba[i]->major, hba[i]->devname);
+		map_major_to_ctlr[hba[i]->major] = 0;
 		release_io_mem(hba[i]);
 		free_hba(i);
                 printk( KERN_ERR "cciss: out of memory");
@@ -3152,16 +3182,16 @@
 
 	cciss_procinit(i);
 
-	q = BLK_DEFAULT_QUEUE(MAJOR_NR + i);
+	q = BLK_DEFAULT_QUEUE(hba[i]->major);
 	q->queuedata = hba[i];
 	blk_init_queue(q, do_cciss_request);
 	blk_queue_bounce_limit(q, hba[i]->pdev->dma_mask);
 	blk_queue_headactive(q, 0);		
 
 	/* fill in the other Kernel structs */
-	blksize_size[MAJOR_NR+i] = hba[i]->blocksizes;
-        hardsect_size[MAJOR_NR+i] = hba[i]->hardsizes;
-        read_ahead[MAJOR_NR+i] = READ_AHEAD;
+	blksize_size[hba[i]->major] = hba[i]->blocksizes;
+        hardsect_size[hba[i]->major] = hba[i]->hardsizes;
+        read_ahead[hba[i]->major] = READ_AHEAD;
 
 	/* Set the pointers to queue functions */ 
 	q->back_merge_fn = cpq_back_merge_fn;
@@ -3170,7 +3200,7 @@
 
 
 	/* Fill in the gendisk data */ 	
-	hba[i]->gendisk.major = MAJOR_NR + i;
+	hba[i]->gendisk.major = hba[i]->major;
 	hba[i]->gendisk.major_name = "cciss";
 	hba[i]->gendisk.minor_shift = NWD_SHIFT;
 	hba[i]->gendisk.max_p = MAX_PART;
@@ -3185,7 +3215,7 @@
 	cciss_geninit(i);
 	for(j=0; j<NWD; j++)
 		register_disk(&(hba[i]->gendisk),
-			MKDEV(MAJOR_NR+i, j <<4), 
+			MKDEV(hba[i]->major, j <<4), 
 			MAX_PART, &cciss_fops, 
 			hba[i]->drv[j].nr_blocks);
 
@@ -3228,7 +3258,8 @@
 	pci_set_drvdata(pdev, NULL);
 	iounmap((void*)hba[i]->vaddr);
 	cciss_unregister_scsi(i);  /* unhook from SCSI subsystem */
-	unregister_blkdev(MAJOR_NR+i, hba[i]->devname);
+	unregister_blkdev(hba[i]->major, hba[i]->devname);
+	map_major_to_ctlr[hba[i]->major] = 0;
 	remove_proc_entry(hba[i]->devname, proc_cciss);	
 	
 
diff -urN linux-2.4.23-pre5/drivers/block/cciss.h linux-2.4.23-pre6/drivers/block/cciss.h
--- linux-2.4.23-pre5/drivers/block/cciss.h	2003-10-01 10:38:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/block/cciss.h	2003-10-01 10:38:45.000000000 -0700
@@ -40,6 +40,7 @@
 struct ctlr_info 
 {
 	int	ctlr;
+	int	major;
 	char	devname[8];
 	char    *product_name;
 	char	firm_ver[4]; // Firmware version 
diff -urN linux-2.4.23-pre5/drivers/block/cciss_scsi.c linux-2.4.23-pre6/drivers/block/cciss_scsi.c
--- linux-2.4.23-pre5/drivers/block/cciss_scsi.c	2003-08-25 04:44:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/block/cciss_scsi.c	2003-10-01 10:38:45.000000000 -0700
@@ -71,16 +71,7 @@
 #endif
 #endif
 
-static struct cciss_scsi_hba_t ccissscsi[MAX_CTLR] = {
-	{ name: "cciss0", ndevices: 0 },
-	{ name: "cciss1", ndevices: 0 },
-	{ name: "cciss2", ndevices: 0 },
-	{ name: "cciss3", ndevices: 0 },
-	{ name: "cciss4", ndevices: 0 },
-	{ name: "cciss5", ndevices: 0 },
-	{ name: "cciss6", ndevices: 0 },
-	{ name: "cciss7", ndevices: 0 },
-};
+static struct cciss_scsi_hba_t ccissscsi[MAX_CTLR];
 
 /* We need one Scsi_Host_Template *per controller* instead of
    the usual one Scsi_Host_Template per controller *type*. This
@@ -92,11 +83,7 @@
    (that's called from cciss.c:cciss_init_one()) */
 
 static
-Scsi_Host_Template driver_template[MAX_CTLR] =
-{
-	CCISS_SCSI, CCISS_SCSI, CCISS_SCSI, CCISS_SCSI,
-	CCISS_SCSI, CCISS_SCSI, CCISS_SCSI, CCISS_SCSI,
-};
+Scsi_Host_Template driver_template[MAX_CTLR];
 
 #pragma pack(1)
 struct cciss_scsi_cmd_stack_elem_t {
@@ -803,13 +790,7 @@
 
 	sh->this_id = SELF_SCSI_ID;
 
-	/* This is a bit kludgey, using the adapter name to figure out */
-	/* which scsi host template we've got, won't scale beyond 9 ctlrs. */
-	i = tpnt->name[5] - '0';
-
-#	if MAX_CTLR > 9
-#		error "cciss_scsi.c: MAX_CTLR > 9, code maintenance needed."
-#	endif
+	i = simple_strtol((char *)&tpnt->name[5], NULL, 10);
 
 	if (i<0 || i>=MAX_CTLR || hba[i] == NULL) {
 		/* we didn't find ourself... we shouldn't get here. */
@@ -1528,9 +1509,10 @@
 	unsigned long flags;
 
 	CPQ_TAPE_LOCK(ctlr, flags);
-	driver_template[ctlr].name = ccissscsi[ctlr].name;
-	driver_template[ctlr].proc_name = ccissscsi[ctlr].name;
-	driver_template[ctlr].module = THIS_MODULE;;
+
+	sprintf( ccissscsi[ctlr].name, "cciss%d", ctlr );
+	
+	init_driver_template(ctlr);
 
 	/* Since this is really a block driver, the SCSI core may not be
 	   initialized yet, in which case, calling scsi_register_module
diff -urN linux-2.4.23-pre5/drivers/block/cciss_scsi.h linux-2.4.23-pre6/drivers/block/cciss_scsi.h
--- linux-2.4.23-pre5/drivers/block/cciss_scsi.h	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.23-pre6/drivers/block/cciss_scsi.h	2003-10-01 10:38:45.000000000 -0700
@@ -89,7 +89,7 @@
 };
 
 struct cciss_scsi_hba_t {
-	char *name;
+	char name[32];
 	int ndevices;
 #define CCISS_MAX_SCSI_DEVS_PER_HBA 16
 	struct cciss_scsi_dev_t dev[CCISS_MAX_SCSI_DEVS_PER_HBA];
diff -urN linux-2.4.23-pre5/drivers/cdrom/cdrom.c linux-2.4.23-pre6/drivers/cdrom/cdrom.c
--- linux-2.4.23-pre5/drivers/cdrom/cdrom.c	2002-11-28 15:53:12.000000000 -0800
+++ linux-2.4.23-pre6/drivers/cdrom/cdrom.c	2003-10-01 10:38:45.000000000 -0700
@@ -1882,20 +1882,26 @@
 	if (cgc->buflen < 0 || cgc->buflen >= 131072)
 		return -EINVAL;
 
-	if ((ubuf = cgc->buffer)) {
+	usense = cgc->sense;
+	cgc->sense = &sense;
+	if (usense && !access_ok(VERIFY_WRITE, usense, sizeof(*usense))) {
+		return -EFAULT;
+	}
+
+	ubuf = cgc->buffer;
+	if (cgc->data_direction == CGC_DATA_READ ||
+	    cgc->data_direction == CGC_DATA_WRITE) {
 		cgc->buffer = kmalloc(cgc->buflen, GFP_KERNEL);
 		if (cgc->buffer == NULL)
 			return -ENOMEM;
 	}
 
-	usense = cgc->sense;
-	cgc->sense = &sense;
-	if (usense && !access_ok(VERIFY_WRITE, usense, sizeof(*usense)))
-		return -EFAULT;
 
 	if (cgc->data_direction == CGC_DATA_READ) {
-		if (!access_ok(VERIFY_READ, ubuf, cgc->buflen))
+		if (!access_ok(VERIFY_READ, ubuf, cgc->buflen)) {
+			kfree(cgc->buffer);
 			return -EFAULT;
+		}
 	} else if (cgc->data_direction == CGC_DATA_WRITE) {
 		if (copy_from_user(cgc->buffer, ubuf, cgc->buflen)) {
 			kfree(cgc->buffer);
@@ -1907,7 +1913,10 @@
 	__copy_to_user(usense, cgc->sense, sizeof(*usense));
 	if (!ret && cgc->data_direction == CGC_DATA_READ)
 		__copy_to_user(ubuf, cgc->buffer, cgc->buflen);
-	kfree(cgc->buffer);
+	if (cgc->data_direction == CGC_DATA_READ ||
+	    cgc->data_direction == CGC_DATA_WRITE) {
+		kfree(cgc->buffer);
+	}
 	return ret;
 }
 
diff -urN linux-2.4.23-pre5/drivers/char/Config.in linux-2.4.23-pre6/drivers/char/Config.in
--- linux-2.4.23-pre5/drivers/char/Config.in	2003-10-01 10:38:42.000000000 -0700
+++ linux-2.4.23-pre6/drivers/char/Config.in	2003-10-01 10:38:45.000000000 -0700
@@ -308,7 +308,7 @@
 
 if [ "$CONFIG_GART_IOMMU" = "y" ]; then
 	bool '/dev/agpgart (AGP Support)' CONFIG_AGP
-	define_bool CONFIG_AGP_AMD_8151 y
+	define_bool CONFIG_AGP_AMD_K8 y
 else
 	tristate '/dev/agpgart (AGP Support)' CONFIG_AGP
 fi      
@@ -318,7 +318,7 @@
    bool '  VIA chipset support' CONFIG_AGP_VIA
    bool '  AMD Irongate, 761, and 762 support' CONFIG_AGP_AMD
    if [ "$CONFIG_GART_IOMMU" != "y" ]; then
-      bool '  AMD 8151 support' CONFIG_AGP_AMD_8151
+      bool '  AMD Opteron/Athlon64 on-CPU GART support' CONFIG_AGP_AMD_K8
    fi   
    bool '  Generic SiS support' CONFIG_AGP_SIS
    bool '  ALI chipset support' CONFIG_AGP_ALI
diff -urN linux-2.4.23-pre5/drivers/char/agp/agp.h linux-2.4.23-pre6/drivers/char/agp/agp.h
--- linux-2.4.23-pre5/drivers/char/agp/agp.h	2003-10-01 10:38:42.000000000 -0700
+++ linux-2.4.23-pre6/drivers/char/agp/agp.h	2003-10-01 10:38:45.000000000 -0700
@@ -172,6 +172,12 @@
 #ifndef PCI_DEVICE_ID_VIA_82C694X_0
 #define PCI_DEVICE_ID_VIA_82C694X_0      0x0605
 #endif
+#ifndef PCI_DEVICE_ID_VIA_8380_0
+#define PCI_DEVICE_ID_VIA_8380_0	0x0204
+#endif
+#ifndef PCI_DEVICE_ID_VIA_8385_0
+#define PCI_DEVICE_ID_VIA_8385_0	0x3188
+#endif 
 #ifndef PCI_DEVICE_ID_INTEL_810_0
 #define PCI_DEVICE_ID_INTEL_810_0       0x7120
 #endif
@@ -511,6 +517,14 @@
 #define NVIDIA_3_APBASE     0x50
 #define NVIDIA_3_APLIMIT    0x54
 
+/* NVIDIA x86-64 registers */
+#define NVIDIA_X86_64_0_APBASE		0x10
+#define NVIDIA_X86_64_1_APBASE1		0x50
+#define NVIDIA_X86_64_1_APLIMIT1	0x54
+#define NVIDIA_X86_64_1_APSIZE		0xa8
+#define NVIDIA_X86_64_1_APBASE2		0xd8
+#define NVIDIA_X86_64_1_APLIMIT2	0xdc
+
 /* HP ZX1 IOC registers */
 #define HP_ZX1_IBASE		0x300
 #define HP_ZX1_IMASK		0x308
diff -urN linux-2.4.23-pre5/drivers/char/agp/agpgart_be.c linux-2.4.23-pre6/drivers/char/agp/agpgart_be.c
--- linux-2.4.23-pre5/drivers/char/agp/agpgart_be.c	2003-10-01 10:38:42.000000000 -0700
+++ linux-2.4.23-pre6/drivers/char/agp/agpgart_be.c	2003-10-01 10:38:45.000000000 -0700
@@ -49,9 +49,6 @@
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/page.h>
-#ifdef CONFIG_AGP_NVIDIA
-    #include <asm/msr.h>
-#endif
 
 #include <linux/agp_backend.h>
 #include "agp.h"
@@ -3403,38 +3400,11 @@
 
 #endif /* CONFIG_AGP_AMD */
 
-#ifdef CONFIG_AGP_AMD_8151
+#ifdef CONFIG_AGP_AMD_K8
 
 /* Begin AMD-8151 support */
 
-static u_int64_t pci_read64 (struct pci_dev *dev, int reg)
-{
-	union {
-		u64 full;
-		struct {
-			u32 high;
-			u32 low;
-		} split;
-	} tmp;
-	pci_read_config_dword(dev, reg, &tmp.split.high);
-	pci_read_config_dword(dev, reg+4, &tmp.split.low);
-	return tmp.full;
-}
-
-static void pci_write64 (struct pci_dev *dev, int reg, u64 value)
-{
-	union {
-		u64 full;
-		struct {
-			u32 high;
-			u32 low;
-		} split;
-	} tmp;
-	tmp.full = value;
-	pci_write_config_dword(dev, reg, tmp.split.high);
-	pci_write_config_dword(dev, reg+4, tmp.split.low);
-}
-
+/* This is misnamed. It is really a generic AMD K8 Northbridge AMD driver. */
 
 static int x86_64_insert_memory(agp_memory * mem, off_t pg_start, int type)
 {
@@ -3442,7 +3412,6 @@
 	void *temp;
 	long tmp;
 	u32 pte;
-	u64 addr;
 
 	temp = agp_bridge.current_size;
 
@@ -3472,9 +3441,8 @@
 	}
 
 	for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
-		addr = agp_bridge.mask_memory(mem->memory[i], mem->type);
+		tmp = agp_bridge.mask_memory(mem->memory[i], mem->type);
 
-		tmp = addr;
 		BUG_ON(tmp & 0xffffff0000000ffc);
 		pte = (tmp & 0x000000ff00000000) >> 28;
 		pte |=(tmp & 0x00000000fffff000);
@@ -3612,10 +3580,7 @@
 
 static int amd_8151_configure(void)
 {
-	struct pci_dev *dev, *hammer=NULL;
-	int current_size;
-	int tmp, tmp2, i;
-	u64 aperbar;
+	struct pci_dev *dev;
 	unsigned long gatt_bus = virt_to_phys(agp_bridge.gatt_table_real);
 
 	/* Configure AGP regs in each x86-64 host bridge. */
@@ -3624,54 +3589,8 @@
 			PCI_FUNC(dev->devfn)==3 &&
 			PCI_SLOT(dev->devfn)>=24 && PCI_SLOT(dev->devfn)<=31) {
 			agp_bridge.gart_bus_addr = amd_x86_64_configure(dev,gatt_bus);
-			hammer = dev;
-
-			/*
-			 * TODO: Cache pci_dev's of x86-64's in private struct to save us
-			 * having to scan the pci list each time.
-			 */
-		}
 	}
-
-	if (hammer == NULL) {
-		return -ENODEV;
 	}
-
-	/* Shadow x86-64 registers into 8151 registers. */
-
-	dev = agp_bridge.dev;
-	if (!dev) 
-		return -ENODEV;
-
-	current_size = amd_x86_64_fetch_size();
-
-	pci_read_config_dword(dev, AMD_8151_APERTURESIZE, &tmp);
-	tmp &= ~(0xfff);
-
-	/* translate x86-64 size bits to 8151 size bits*/
-	for (i=0 ; i<7; i++) {
-		if (amd_8151_sizes[i].size == current_size)
-			tmp |= (amd_8151_sizes[i].size_value) << 3;
-	}
-	pci_write_config_dword(dev, AMD_8151_APERTURESIZE, tmp);
-
-	pci_read_config_dword (hammer, AMD_X86_64_GARTAPERTUREBASE, &tmp);
-	aperbar = pci_read64 (dev, AMD_8151_VMAPERTURE);
-	aperbar |= (tmp & 0x7fff) <<25;
-	aperbar &= 0x000000ffffffffff;
-	aperbar |= 1<<2;	/* This address is a 64bit ptr FIXME: Make conditional in 32bit mode */
-	pci_write64 (dev, AMD_8151_VMAPERTURE, aperbar);
-
-	pci_read_config_dword(dev, AMD_8151_AGP_CTL , &tmp);
-	tmp &= ~(AMD_8151_GTLBEN | AMD_8151_APEREN);
-	
-	pci_read_config_dword(hammer, AMD_X86_64_GARTAPERTURECTL, &tmp2);
-	if (tmp2 & AMD_X86_64_GARTEN)
-		tmp |= AMD_8151_APEREN;
-	// FIXME: bit 7 of AMD_8151_AGP_CTL (GTLBEN) must be copied if set.
-	// But where is it set ?
-	pci_write_config_dword(dev, AMD_8151_AGP_CTL, tmp);
-
 	return 0;
 }
 
@@ -3690,15 +3609,6 @@
 			tmp &= ~(AMD_X86_64_GARTEN);
 			pci_write_config_dword (dev, AMD_X86_64_GARTAPERTURECTL, tmp);
 		}
-
-		/* Now shadow the disable in the 8151 */
-		if (dev->vendor == PCI_VENDOR_ID_AMD &&
-			dev->device == PCI_DEVICE_ID_AMD_8151_0) {
-
-			pci_read_config_dword (dev, AMD_8151_AGP_CTL, &tmp);
-			tmp &= ~(AMD_8151_APEREN);	
-			pci_write_config_dword (dev, AMD_8151_AGP_CTL, tmp);
-		}
 	}
 }
 
@@ -3712,7 +3622,7 @@
 
 static gatt_mask amd_8151_masks[] =
 {
-	{0x00000001, 0}
+	{0, 0}
 };
 
 
@@ -3747,7 +3657,7 @@
 				printk (KERN_INFO "AGP: Found AGPv3 capable device at %d:%d:%d\n",
 					device->bus->number, PCI_FUNC(device->devfn), PCI_SLOT(device->devfn));
 			} else {
-				printk (KERN_INFO "AGP: Meh. version %x AGP device found.\n", scratch);
+				printk (KERN_INFO "AGP: Version %x AGP device found.\n", scratch);
 			}
 		}
 	}
@@ -3866,7 +3776,165 @@
 	(void) pdev; /* unused */
 }
 
-#endif /* CONFIG_AGP_AMD_8151 */
+/* NVIDIA x86-64 chipset support */
+ 
+
+static struct _nvidia_x86_64_private {
+	struct pci_dev *dev_1;
+} nvidia_x86_64_private;
+
+
+static aper_size_info_32 nvidia_x86_64_sizes[5] =
+{
+	{512,  131072, 7, 0x00000000 },
+	{256,  65536,  6, 0x00000008 },
+	{128,  32768,  5, 0x0000000C },
+	{64,   16384,  4, 0x0000000E },
+	{32,   8192,   3, 0x0000000F }
+};
+
+
+static int nvidia_x86_64_configure(void)
+{
+	struct pci_dev *dev, *hammer=NULL;
+	int i, current_size;
+	u32 tmp, apbase, apbar, aplimit;
+	unsigned long gatt_bus = virt_to_phys(agp_bridge.gatt_table_real);
+
+	if (!agp_bridge.dev) 
+		return -ENODEV;
+
+	/* configure AGP regs in each x86-64 host bridge */
+	pci_for_each_dev(dev) {
+		if (dev->bus->number==0 &&
+			PCI_FUNC(dev->devfn)==3 &&
+			PCI_SLOT(dev->devfn)>=24 && PCI_SLOT(dev->devfn)<=31) {
+			agp_bridge.gart_bus_addr = amd_x86_64_configure(dev,gatt_bus);
+			hammer = dev;
+		}
+	}
+	if (hammer == NULL)
+		return -ENODEV;
+
+	/* translate x86-64 aperture size to NVIDIA aperture size */
+	current_size = amd_x86_64_fetch_size();
+	for (i = 0 ; i < agp_bridge.num_aperture_sizes; i++) {
+		if (nvidia_x86_64_sizes[i].size == current_size)
+			break;
+	}
+	/* if x86-64 size does not match any NVIDIA size, exit here */
+	if (i == agp_bridge.num_aperture_sizes)
+		return -ENODEV;
+	pci_read_config_dword(nvidia_x86_64_private.dev_1, NVIDIA_X86_64_1_APSIZE, &tmp);
+	tmp &= ~(0xf);
+	tmp |= nvidia_x86_64_sizes[i].size_value;
+	pci_write_config_dword(nvidia_x86_64_private.dev_1, NVIDIA_X86_64_1_APSIZE, tmp);
+
+	/* shadow x86-64 registers into NVIDIA registers */
+	pci_read_config_dword (hammer, AMD_X86_64_GARTAPERTUREBASE, &apbase);
+	/* if x86-64 aperture base is beyond 4G, exit here */
+	if ( (apbase & 0x7fff) >> (32 - 25) )
+		 return -ENODEV;
+	apbase = (apbase & 0x7fff) << 25;
+
+	/* AK: most likely the shadow into the primary device is not needed */
+
+	pci_read_config_dword(agp_bridge.dev, NVIDIA_X86_64_0_APBASE, &apbar);
+	apbar &= ~PCI_BASE_ADDRESS_MEM_MASK;
+	apbar |= apbase;
+	pci_write_config_dword(agp_bridge.dev, NVIDIA_X86_64_0_APBASE, apbar);
+
+	/* Shadow into secondary device looks dubious, but we keep it for now.
+	   If these two could be dropped then the NForce3 code path could
+	   be just folded into the generic functions above. */
+
+	aplimit = apbase + (current_size * 1024 * 1024) - 1;
+	pci_write_config_dword(nvidia_x86_64_private.dev_1, NVIDIA_X86_64_1_APBASE1, apbase);
+	pci_write_config_dword(nvidia_x86_64_private.dev_1, NVIDIA_X86_64_1_APLIMIT1, aplimit);
+	pci_write_config_dword(nvidia_x86_64_private.dev_1, NVIDIA_X86_64_1_APBASE2, apbase);
+	pci_write_config_dword(nvidia_x86_64_private.dev_1, NVIDIA_X86_64_1_APLIMIT2, aplimit);
+
+	/* Original driver updated the IORR here, but AMD documentation
+	   explicitely discourages this for something already covered by the GART. */
+	
+	return 0;
+}
+
+
+static void nvidia_x86_64_cleanup(void)
+{
+	struct pci_dev *dev;
+	u32 tmp;
+
+	pci_for_each_dev(dev) {
+		/* disable gart translation */
+		if (dev->bus->number==0 && PCI_FUNC(dev->devfn)==3 &&
+		    (PCI_SLOT(dev->devfn) >=24) && (PCI_SLOT(dev->devfn) <=31)) {
+
+			pci_read_config_dword (dev, AMD_X86_64_GARTAPERTURECTL, &tmp);
+			tmp &= ~(AMD_X86_64_GARTEN);
+			pci_write_config_dword (dev, AMD_X86_64_GARTAPERTURECTL, tmp);
+		}
+	}
+}
+
+
+static unsigned long nvidia_x86_64_mask_memory(unsigned long addr, int type)
+{
+	return addr | agp_bridge.masks[0].mask;
+}
+
+
+static gatt_mask nvidia_x86_64_masks[] =
+{
+	{0x00000001, 0}
+};
+
+
+static int __init nvidia_x86_64_setup (struct pci_dev *pdev)
+{
+	nvidia_x86_64_private.dev_1 =
+		pci_find_slot((unsigned int)pdev->bus->number, PCI_DEVFN(11, 0));
+
+	if (nvidia_x86_64_private.dev_1 == NULL) {
+		printk(KERN_INFO PFX "agpgart: Detected an NVIDIA "
+			"nForce3 chipset, but could not find "
+			"the secondary device.\n");
+		agp_bridge.type = NOT_SUPPORTED;
+		return -ENODEV;
+	}
+
+	agp_bridge.masks = nvidia_x86_64_masks;
+	agp_bridge.aperture_sizes = (void *) nvidia_x86_64_sizes;
+	agp_bridge.size_type = U32_APER_SIZE;
+	agp_bridge.num_aperture_sizes = 5;
+	agp_bridge.dev_private_data = NULL;
+	agp_bridge.needs_scratch_page = FALSE;
+	agp_bridge.configure = nvidia_x86_64_configure;
+	agp_bridge.fetch_size = amd_x86_64_fetch_size;
+	agp_bridge.cleanup = nvidia_x86_64_cleanup;
+	agp_bridge.tlb_flush = amd_x86_64_tlbflush;
+	agp_bridge.mask_memory = nvidia_x86_64_mask_memory;
+	agp_bridge.agp_enable = agp_x86_64_agp_enable;
+	agp_bridge.cache_flush = global_cache_flush;
+	agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
+	agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
+	agp_bridge.insert_memory = x86_64_insert_memory;
+	agp_bridge.remove_memory = agp_generic_remove_memory;
+	agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
+	agp_bridge.free_by_type = agp_generic_free_by_type;
+	agp_bridge.agp_alloc_page = agp_generic_alloc_page;
+	agp_bridge.agp_destroy_page = agp_generic_destroy_page;
+	agp_bridge.suspend = agp_generic_suspend;
+	agp_bridge.resume = agp_generic_resume;
+	agp_bridge.cant_use_aperture = 0;
+
+	return 0;
+	
+	(void) pdev; /* unused */
+}
+
+#endif /* CONFIG_AGP_AMD_K8 */
 
 #ifdef CONFIG_AGP_ALI
 
@@ -4741,52 +4809,6 @@
 	return 0;
 }
 
-#define SYSCFG          0xC0010010
-#define IORR_BASE0      0xC0010016
-#define IORR_MASK0      0xC0010017
-#define AMD_K7_NUM_IORR 2
-
-static int nvidia_init_iorr(u32 base, u32 size)
-{
-	u32 base_hi, base_lo;
-	u32 mask_hi, mask_lo;
-	u32 sys_hi, sys_lo;
-	u32 iorr_addr, free_iorr_addr;
-
-	/* Find the iorr that is already used for the base */
-	/* If not found, determine the uppermost available iorr */
-	free_iorr_addr = AMD_K7_NUM_IORR;
-	for(iorr_addr = 0; iorr_addr < AMD_K7_NUM_IORR; iorr_addr++) {
-		rdmsr(IORR_BASE0 + 2 * iorr_addr, base_lo, base_hi);
-		rdmsr(IORR_MASK0 + 2 * iorr_addr, mask_lo, mask_hi);
-
-		if ((base_lo & 0xfffff000) == (base & 0xfffff000))
-			break;
-
-		if ((mask_lo & 0x00000800) == 0)
-			free_iorr_addr = iorr_addr;
-	}
-	
-	if (iorr_addr >= AMD_K7_NUM_IORR) {
-		iorr_addr = free_iorr_addr;
-		if (iorr_addr >= AMD_K7_NUM_IORR)
-			return -EINVAL;
-	}
-
-    base_hi = 0x0;
-    base_lo = (base & ~0xfff) | 0x18;
-    mask_hi = 0xf;
-    mask_lo = ((~(size - 1)) & 0xfffff000) | 0x800;
-    wrmsr(IORR_BASE0 + 2 * iorr_addr, base_lo, base_hi);
-    wrmsr(IORR_MASK0 + 2 * iorr_addr, mask_lo, mask_hi);
-
-    rdmsr(SYSCFG, sys_lo, sys_hi);
-    sys_lo |= 0x00100000;
-    wrmsr(SYSCFG, sys_lo, sys_hi);
-
-	return 0;
-}
-
 static int nvidia_configure(void)
 {
 	int i, rc, num_dirs;
@@ -4800,7 +4822,7 @@
 	pci_write_config_byte(agp_bridge.dev, NVIDIA_0_APSIZE,
 		current_size->size_value);
 
-    /* address to map to */
+	/* address to map to */
 	pci_read_config_dword(agp_bridge.dev, NVIDIA_0_APBASE, &apbase);
 	apbase &= PCI_BASE_ADDRESS_MEM_MASK;
 	agp_bridge.gart_bus_addr = apbase;
@@ -4809,8 +4831,9 @@
 	pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_APLIMIT, aplimit);
 	pci_write_config_dword(nvidia_private.dev_3, NVIDIA_3_APBASE, apbase);
 	pci_write_config_dword(nvidia_private.dev_3, NVIDIA_3_APLIMIT, aplimit);
-	if (0 != (rc = nvidia_init_iorr(apbase, current_size->size * 1024 * 1024)))
-		return rc;
+
+	/* The original driver changed the IORR. We don't do that
+	   because this should be handled by the BIOS. */
 
 	/* directory size is 64k */
 	num_dirs = current_size->size / 64;
@@ -4868,10 +4891,6 @@
 	previous_size = A_SIZE_8(agp_bridge.previous_size);
 	pci_write_config_byte(agp_bridge.dev, NVIDIA_0_APSIZE,
 		previous_size->size_value);
-
-	/* restore iorr for previous aperture size */
-	nvidia_init_iorr(agp_bridge.gart_bus_addr,
-		previous_size->size * 1024 * 1024);
 }
 
 static void nvidia_tlbflush(agp_memory * mem)
@@ -5938,6 +5957,20 @@
 		"AMD",
 		"761",
 		amd_irongate_setup },
+#endif
+#ifdef CONFIG_AGP_AMD_K8
+	{ PCI_DEVICE_ID_AMD_8151_0,
+		PCI_VENDOR_ID_AMD,
+		AMD_8151,
+		"AMD",
+		"On-CPU GART",
+		amd_8151_setup },
+
+	/* Note: when adding more PCI-IDs for 8151 compatible bridges
+	   add them to the end of their vendor lists, not here.
+	   This list has to be ordered by vendor. */
+#endif /* CONFIG_AGP_AMD */
+#ifdef CONFIG_AGP_AMD
 	{ 0,
 		PCI_VENDOR_ID_AMD,
 		AMD_GENERIC,
@@ -5946,15 +5979,6 @@
 		amd_irongate_setup },
 #endif /* CONFIG_AGP_AMD */
 
-#ifdef CONFIG_AGP_AMD_8151
-	{ PCI_DEVICE_ID_AMD_8151_0,
-		PCI_VENDOR_ID_AMD,
-		AMD_8151,
-		"AMD",
-		"8151",
-		amd_8151_setup },
-#endif /* CONFIG_AGP_AMD */
-
 #ifdef CONFIG_AGP_INTEL
 	{ PCI_DEVICE_ID_INTEL_82443LX_0,
 		PCI_VENDOR_ID_INTEL,
@@ -6178,6 +6202,16 @@
 		"SiS",
                 "550",
 		sis_generic_setup },
+#endif
+#ifdef CONFIG_AGP_AMD_K8
+	{ PCI_DEVICE_ID_SI_755,
+		PCI_VENDOR_ID_SI,
+		AMD_8151,
+		"AMD",
+		"On-CPU GART",
+		amd_8151_setup },
+#endif
+#ifdef CONFIG_AGP_SIS
 	{ 0,
 		PCI_VENDOR_ID_SI,
 		SIS_GENERIC,
@@ -6265,6 +6299,24 @@
 		"Via",
 		"Apollo P4M266",
 		via_generic_setup },
+#endif
+#ifdef CONFIG_AGP_AMD_K8
+ 	{ PCI_DEVICE_ID_VIA_8380_0,
+ 		PCI_VENDOR_ID_VIA,
+ 		AMD_8151,
+ 		"AMD",
+ 		"On-CPU GART",
+ 		amd_8151_setup },
+
+ 	/* VIA K8T800 */
+ 	{ PCI_DEVICE_ID_VIA_8385_0,
+  		PCI_VENDOR_ID_VIA,
+  		AMD_8151,
+  		"AMD",
+  		"On-CPU GART",
+ 		amd_8151_setup },		
+#endif
+#ifdef CONFIG_AGP_VIA
 	{ 0,
 		PCI_VENDOR_ID_VIA,
 		VIA_GENERIC,
@@ -6286,6 +6338,16 @@
 		"NVIDIA",
 		"nForce2",
 		nvidia_generic_setup },
+#endif
+#ifdef CONFIG_AGP_AMD_K8
+	{ PCI_DEVICE_ID_NVIDIA_NFORCE3,
+		PCI_VENDOR_ID_NVIDIA,
+		NVIDIA_NFORCE3,
+		"NVIDIA",
+		"nForce3/K8 On-CPU GART",
+		nvidia_x86_64_setup },
+#endif
+#ifdef CONFIG_AGP_NVIDIA
 	{ 0,
 		PCI_VENDOR_ID_NVIDIA,
 		NVIDIA_GENERIC,
@@ -6412,7 +6474,11 @@
 	}
 
 	printk(KERN_ERR PFX "Unsupported %s chipset (device id: %04x),"
+#ifdef MODULE
 	       " you might want to try agp_try_unsupported=1.\n",
+#else
+	       " you might want to boot with agp=try_unsupported\n",
+#endif
 	       agp_bridge_info[i].vendor_name, pdev->device);
 	return -ENODEV;
 }
@@ -6435,6 +6501,31 @@
 
 	agp_bridge.dev = dev;
 
+#ifdef CONFIG_AGP_AMD_K8
+	/* If there is any K8 northbridge in the system always use the K8 driver */
+	if (agp_try_unsupported
+	    && pci_find_device(PCI_VENDOR_ID_AMD, 0x1103, NULL)
+	    && !pci_find_device(PCI_VENDOR_ID_NVIDIA, 
+				PCI_DEVICE_ID_NVIDIA_NFORCE3,
+				NULL)) { 
+
+		/* find capndx */
+		cap_ptr = pci_find_capability(dev, PCI_CAP_ID_AGP);
+		if (cap_ptr == 0x00)
+			return -ENODEV;
+		agp_bridge.capndx = cap_ptr;
+		
+		/* Fill in the mode register */
+		pci_read_config_dword(agp_bridge.dev,
+				      agp_bridge.capndx + 4,
+				      &agp_bridge.mode);
+		
+		printk(KERN_INFO PFX "Detected GART in AMD K8 Northbridge\n"); 
+		agp_bridge.type = AMD_8151; 
+		return amd_8151_setup(dev);
+	}					
+#endif
+
 	/* Need to test for I810 here */
 #ifdef CONFIG_AGP_I810
 	if (dev->vendor == PCI_VENDOR_ID_INTEL) {
@@ -6892,10 +6983,26 @@
 	&agp_copy_info
 };
 
+static int __initdata agp_off = 0; 
+int __init agp_setup(char *s) 
+{ 
+	if (!strcmp(s,"off"))
+		agp_off = 1;
+	if (!strcmp(s,"try_unsupported"))
+		agp_try_unsupported = 1;
+	return 0;		
+} 
+__setup("agp=", agp_setup); 
+
 int __init agp_init(void)
 {
 	int ret_val;
 
+	if (agp_off) { 
+		printk("AGP disabled\n"); 
+		return -1;
+	} 
+
 	printk(KERN_INFO "Linux agpgart interface v%d.%d (c) Jeff Hartmann\n",
 	       AGPGART_VERSION_MAJOR, AGPGART_VERSION_MINOR);
 
diff -urN linux-2.4.23-pre5/drivers/char/sh-sci.c linux-2.4.23-pre6/drivers/char/sh-sci.c
--- linux-2.4.23-pre5/drivers/char/sh-sci.c	2003-08-25 04:44:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/char/sh-sci.c	2003-10-01 10:38:49.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: sh-sci.c,v 1.1.1.1.2.7 2003/07/16 18:45:31 yoshii Exp $
+/* $Id: sh-sci.c,v 1.1.1.1.2.10 2003/09/17 23:38:56 davidm-sf Exp $
  *
  *  linux/drivers/char/sh-sci.c
  *
@@ -6,6 +6,7 @@
  *  Copyright (C) 1999, 2000  Niibe Yutaka
  *  Copyright (C) 2000  Sugioka Toshinobu
  *  Modified to support multiple serial ports. Stuart Menefy (May 2000).
+ *  Modified to support SecureEdge. David McCullough (2002) 
  *  Modified to support SH7300 SCIF. Takashi Kusuda (Jun 2003).
  *
  * TTY code is based on sx.c (Specialix SX driver) by:
@@ -239,6 +240,51 @@
 #endif /* CONFIG_SERIAL_CONSOLE */
 
 
+#if defined(CONFIG_SH_SECUREEDGE5410)
+
+struct timer_list sci_timer_struct;
+static unsigned char sci_dcdstatus[2];
+
+/*
+ * This subroutine is called when the RS_TIMER goes off. It is used
+ * to monitor the state of the DCD lines - since they have no edge
+ * sensors and interrupt generators.
+ */
+static void sci_timer(unsigned long data)
+{
+	unsigned short s, i;
+	unsigned char  dcdstatus[2];
+
+	s = SECUREEDGE_READ_IOPORT();
+	dcdstatus[0] = !(s & 0x10);
+	dcdstatus[1] = !(s & 0x1);
+
+	for (i = 0; i < 2; i++) {
+		if (dcdstatus[i] != sci_dcdstatus[i]) {
+			if (sci_ports[i].gs.count != 0) {
+				if (sci_ports[i].gs.flags & ASYNC_CHECK_CD) {
+					if (dcdstatus[i]) { /* DCD has gone high */
+						wake_up_interruptible(&sci_ports[i].gs.open_wait);
+					} else if (!((sci_ports[i].gs.flags&ASYNC_CALLOUT_ACTIVE) &&
+							(sci_ports[i].gs.flags & ASYNC_CALLOUT_NOHUP))) {
+						if (sci_ports[i].gs.tty)
+							tty_hangup(sci_ports[i].gs.tty);
+					}
+				}
+			}
+		}
+		sci_dcdstatus[i] = dcdstatus[i];
+	}
+
+	sci_timer_struct.expires = jiffies + HZ/25;
+	add_timer(&sci_timer_struct);
+}
+
+#endif
+
+
+
+
 #ifdef CONFIG_SH_KGDB
 
 /* Is the SCI ready, ie is there a char waiting? */
@@ -359,7 +405,7 @@
 		/* We need to set SCPCR to enable RTS/CTS */
 		data = ctrl_inw(SCPCR);
 		/* Clear out SCP7MD1,0, SCP6MD1,0, SCP4MD1,0*/
-		ctrl_outw(data&0x0fcf, SCPCR);
+		ctrl_outw(data&0x0cff, SCPCR);
 	}
 	if (cflag & CRTSCTS)
 		fcr_val |= SCFCR_MCE;
@@ -370,7 +416,7 @@
 		data = ctrl_inw(SCPCR);
 		/* Clear out SCP7MD1,0, SCP4MD1,0,
 		   Set SCP6MD1,0 = {01} (output)  */
-		ctrl_outw((data&0x0fcf)|0x1000, SCPCR);
+		ctrl_outw((data&0x0cff)|0x1000, SCPCR);
 
 		data = ctrl_inb(SCPDR);
 		/* Set /RTS2 (bit6) = 0 */
@@ -413,7 +459,29 @@
 	/* This routine is used for seting signals of: DTR, DCD, CTS/RTS */
 	/* We use SCIF's hardware for CTS/RTS, so don't need any for that. */
 	/* If you have signals for DTR and DCD, please implement here. */
-	;
+
+#if defined(CONFIG_SH_SECUREEDGE5410)
+	int flags;
+
+	save_and_cli(flags);
+	if (port == &sci_ports[1]) { /* port 1 only */
+		if (dtr == 0)
+			SECUREEDGE_WRITE_IOPORT(0x0080, 0x0080);
+		else if (dtr == 1)
+			SECUREEDGE_WRITE_IOPORT(0x0000, 0x0080);
+	}
+	if (port == &sci_ports[0]) { /* port 0 only */
+		if (dtr == 0)
+			SECUREEDGE_WRITE_IOPORT(0x0200, 0x0200);
+		else if (dtr == 1)
+			SECUREEDGE_WRITE_IOPORT(0x0000, 0x0200);
+		if (rts == 0)
+			SECUREEDGE_WRITE_IOPORT(0x0100, 0x0100);
+		else if (rts == 1)
+			SECUREEDGE_WRITE_IOPORT(0x0000, 0x0100);
+	}
+	restore_flags(flags);
+#endif
 }
 
 static int sci_getsignals(struct sci_port *port)
@@ -421,15 +489,34 @@
 	/* This routine is used for geting signals of: DTR, DCD, DSR, RI,
 	   and CTS/RTS */
 
+#if defined(CONFIG_SH_SECUREEDGE5410)
+	if (port == &sci_ports[1]) { /* port 1 only */
+		unsigned short s = SECUREEDGE_READ_IOPORT();
+		int rc = TIOCM_RTS|TIOCM_DSR|TIOCM_CTS;
+
+		if ((s & 0x0001) == 0)
+			rc |= TIOCM_CAR;
+		if ((SECUREEDGE_READ_IOPORT() & 0x0080) == 0)
+			rc |= TIOCM_DTR;
+		return(rc);
+	}
+	if (port == &sci_ports[0]) { /* port 0 only */
+		unsigned short s = SECUREEDGE_READ_IOPORT();
+		int rc = TIOCM_DSR;
+
+		if ((s & 0x0010) == 0)
+			rc |= TIOCM_CAR;
+		if ((s & 0x0004) == 0)
+			rc |= TIOCM_CTS;
+		if ((SECUREEDGE_READ_IOPORT() & 0x0200) == 0)
+			rc |= TIOCM_DTR;
+		if ((SECUREEDGE_READ_IOPORT() & 0x0100) == 0)
+			rc |= TIOCM_RTS;
+		return(rc);
+	}
+#endif
+
 	return TIOCM_DTR|TIOCM_RTS|TIOCM_DSR;
-/*
-	(((o_stat & OP_DTR)?TIOCM_DTR:0) |
-	 ((o_stat & OP_RTS)?TIOCM_RTS:0) |
-	 ((i_stat & IP_CTS)?TIOCM_CTS:0) |
-	 ((i_stat & IP_DCD)?TIOCM_CAR:0) |
-	 ((i_stat & IP_DSR)?TIOCM_DSR:0) |
-	 ((i_stat & IP_RI) ?TIOCM_RNG:0)
-*/
 }
 
 static void sci_set_baud(struct sci_port *port, int baud)
@@ -458,6 +545,11 @@
 	case 57600:
 		t = BPS_57600;
 		break;
+	case 230400:
+		if (BPS_230400 != BPS_115200) {
+			t = BPS_230400;
+			break;
+		}
 	default:
 		printk(KERN_INFO "sci: unsupported baud rate: %d, using 115200 instead.\n", baud);
 	case 115200:
@@ -513,6 +605,11 @@
 
 	port->init_pins(port, cflag);
 	sci_out(port, SCSCR, SCSCR_INIT(port));
+
+	if (cflag & CLOCAL)
+		port->gs.flags &= ~ASYNC_CHECK_CD;
+	else
+		port->gs.flags |= ASYNC_CHECK_CD;
 }
 
 static int sci_set_real_termios(void *ptr)
@@ -525,22 +622,6 @@
 		sci_enable_rx_interrupts(port);
 	}
 
-	/* Tell line discipline whether we will do input cooking */
-	if (I_OTHER(port->gs.tty))
-		clear_bit(TTY_HW_COOK_IN, &port->gs.tty->flags);
-	else
-		set_bit(TTY_HW_COOK_IN, &port->gs.tty->flags);
-
-/* Tell line discipline whether we will do output cooking.
- * If OPOST is set and no other output flags are set then we can do output
- * processing.  Even if only *one* other flag in the O_OTHER group is set
- * we do cooking in software.
- */
-	if (O_OPOST(port->gs.tty) && !O_OTHER(port->gs.tty))
-		set_bit(TTY_HW_COOK_OUT, &port->gs.tty->flags);
-	else
-		clear_bit(TTY_HW_COOK_OUT, &port->gs.tty->flags);
-
 	return 0;
 }
 
@@ -561,8 +642,8 @@
 
 static void sci_transmit_chars(struct sci_port *port)
 {
-	int count, i;
-	int txroom;
+	unsigned int count, i;
+	unsigned int txroom;
 	unsigned long flags;
 	unsigned short status;
 	unsigned short ctrl;
@@ -645,7 +726,7 @@
 static inline void sci_receive_chars(struct sci_port *port,
 				     struct pt_regs *regs)
 {
-	int i, count;
+	int count;
 	struct tty_struct *tty;
 	int copied=0;
 	unsigned short status;
@@ -655,6 +736,7 @@
 		return;
 
 	tty = port->gs.tty;
+
 	while (1) {
 		if (port->type == PORT_SCIF) {
 #if defined(CONFIG_CPU_SUBTYPE_SH7300)
@@ -666,34 +748,41 @@
 			count = (sci_in(port, SCxSR)&SCxSR_RDxF(port))?1:0;
 		}
 
-		/* Don't copy more bytes than there is room for in the buffer */
-		if (tty->flip.count + count > TTY_FLIPBUF_SIZE)
-			count = TTY_FLIPBUF_SIZE - tty->flip.count;
+		/* we must clear RDF or we get stuck in the interrupt for ever */
+		sci_in(port, SCxSR); /* dummy read */
+		sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
 
 		/* If for any reason we can't copy more data, we're done! */
 		if (count == 0)
 			break;
 
 		if (port->type == PORT_SCI) {
-			tty->flip.char_buf_ptr[0] = sci_in(port, SCxRDR);
-			tty->flip.flag_buf_ptr[0] = TTY_NORMAL;
+			if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+				*tty->flip.char_buf_ptr++ = sci_in(port, SCxRDR);
+				*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
+				tty->flip.count++;
+				port->icount.rx++;
+				copied++;
+				count--;
+			}
 		} else {
-			for (i=0; i<count; i++) {
+			while (count > 0 && tty->flip.count < TTY_FLIPBUF_SIZE){
 				char c = sci_in(port, SCxRDR);
 				status = sci_in(port, SCxSR);
+
 #if defined(__SH3__)
 				/* Skip "chars" during break */
 				if (port->break_flag) {
 					if ((c == 0) &&
 					    (status & SCxSR_FER(port))) {
-						count--; i--;
+						count--;
 						continue;
 					}
 					/* Nonzero => end-of-break */
 					dprintk("scif: debounce<%02x>\n", c);
 					port->break_flag = 0;
 					if (STEPFN(c)) {
-						count--; i--;
+						count--;
 						continue;
 					}
 				}
@@ -706,7 +795,7 @@
 						handle_sysrq(c, regs,
 							     NULL, NULL);
 						break_pressed = 0;
-						count--; i--;
+						count--;
 						continue;
 					} else if (c != 0) {
 						break_pressed = 0;
@@ -715,29 +804,31 @@
 #endif /* CONFIG_SERIAL_CONSOLE && CONFIG_MAGIC_SYSRQ */
 
 				/* Store data and status */
-				tty->flip.char_buf_ptr[i] = c;
+				*tty->flip.char_buf_ptr++ = c;
+
 				if (status&SCxSR_FER(port)) {
-					tty->flip.flag_buf_ptr[i] = TTY_FRAME;
+					*tty->flip.flag_buf_ptr++ = TTY_FRAME;
 					dprintk("sci: frame error\n");
 				} else if (status&SCxSR_PER(port)) {
-					tty->flip.flag_buf_ptr[i] = TTY_PARITY;
+					*tty->flip.flag_buf_ptr++ = TTY_PARITY;
 					dprintk("sci: parity error\n");
 				} else {
-					tty->flip.flag_buf_ptr[i] = TTY_NORMAL;
+					*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
 				}
+				tty->flip.count++;
+				port->icount.rx++;
+				copied++;
+				count--;
 			}
 		}
 
-		sci_in(port, SCxSR); /* dummy read */
-		sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
-
-		/* Update the kernel buffer end */
-		tty->flip.count += count;
-		tty->flip.char_buf_ptr += count;
-		tty->flip.flag_buf_ptr += count;
-
-		copied += count;
-		port->icount.rx += count;
+		/* drop any remaining chars,  we are full */
+		if (count > 0) {
+			/* force an overrun error on last received char */
+			tty->flip.flag_buf_ptr[TTY_FLIPBUF_SIZE - 1] = TTY_OVERRUN;
+			while (count-- > 0)
+				(void) sci_in(port, SCxRDR);
+		}
 	}
 
 	if (copied)
@@ -821,9 +912,11 @@
 		*tty->flip.flag_buf_ptr++ = TTY_BREAK;
 		dprintk("sci: BREAK detected\n");
 	}
+#if defined(CONFIG_CPU_SH3) || defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
  break_continue:
+#endif
 
-#if defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_ST40)
+#if defined(CONFIG_CPU_SUBTYPE_SH7750) || defined (CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_ST40)
 	/* XXX: Handle SCIF overrun error */
 	if (port->type == PORT_SCIF && (sci_in(port, SCLSR) & SCIF_ORER) != 0) {
 		sci_out(port, SCLSR, 0);
@@ -994,6 +1087,15 @@
 static int sci_get_CD(void * ptr)
 {
 	/* If you have signal for CD (Carrier Detect), please change here. */
+
+#if defined(CONFIG_SH_SECUREEDGE5410)
+	struct sci_port *port = ptr;
+
+	if (port == &sci_ports[0] || port == &sci_ports[1])
+		if ((sci_getsignals(port) & TIOCM_CAR) == 0)
+			return 0;
+#endif
+
 	return 1;
 }
 
@@ -1409,6 +1511,18 @@
 		       (port->type == PORT_SCI) ? "SCI" : "SCIF");
 	}
 
+#if defined(CONFIG_SH_SECUREEDGE5410)
+	init_timer(&sci_timer_struct);
+	sci_timer_struct.function = sci_timer;
+	sci_timer_struct.data = 0;
+	sci_timer_struct.expires = jiffies + HZ/25;
+	add_timer(&sci_timer_struct);
+
+	j = SECUREEDGE_READ_IOPORT();
+	sci_dcdstatus[0] = !(j & 0x10);
+	sci_dcdstatus[1] = !(j & 0x1);
+#endif
+
 	sci_init_drivers();
 
 #ifdef CONFIG_SH_STANDARD_BIOS
@@ -1425,6 +1539,9 @@
 
 void cleanup_module(void)
 {
+#if defined(CONFIG_SH_SECUREEDGE5410)
+	del_timer(&sci_timer_struct);
+#endif
 	tty_unregister_driver(&sci_driver);
 	tty_unregister_driver(&sci_callout_driver);
 }
@@ -1495,6 +1612,9 @@
 		case 115200:
 			cflag |= B115200;
 			break;
+		case 230400:
+			cflag |= B230400;
+			break;
 		case 9600:
 		default:
 			cflag |= B9600;
diff -urN linux-2.4.23-pre5/drivers/char/sh-sci.h linux-2.4.23-pre6/drivers/char/sh-sci.h
--- linux-2.4.23-pre5/drivers/char/sh-sci.h	2003-08-25 04:44:41.000000000 -0700
+++ linux-2.4.23-pre6/drivers/char/sh-sci.h	2003-10-01 10:38:49.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: sh-sci.h,v 1.1.1.1.2.3 2003/07/16 18:46:06 yoshii Exp $
+/* $Id: sh-sci.h,v 1.1.1.1.2.5 2003/09/03 08:37:41 kkojima Exp $
  *
  *  linux/drivers/char/sh-sci.h
  *
@@ -72,6 +72,15 @@
 	0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ : \
 	0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ )
 # define SCI_AND_SCIF
+#elif defined(CONFIG_CPU_SUBTYPE_SH4_202)
+# define SCI_NPORTS 1
+# define SCI_INIT { \
+  { {}, PORT_SCIF, 0xFFE80000, SH4_SCIF_IRQS, sci_init_pins_scif }  \
+}
+# define SCSPTR2 0xFFE80020 /* 16 bit SCIF */
+# define SCIF_ORER 0x0001   /* overrun error bit */
+# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
+# define SCIF_ONLY
 #elif defined(CONFIG_CPU_SUBTYPE_ST40)
 # define SCI_NPORTS 2
 # define SCI_INIT { \
@@ -210,29 +219,6 @@
 /* Generic serial flags */
 #define SCI_RX_THROTTLE		0x0000001
 
-/* generic serial tty */
-#define O_OTHER(tty)    \
-      ((O_OLCUC(tty))  ||\
-      (O_ONLCR(tty))   ||\
-      (O_OCRNL(tty))   ||\
-      (O_ONOCR(tty))   ||\
-      (O_ONLRET(tty))  ||\
-      (O_OFILL(tty))   ||\
-      (O_OFDEL(tty))   ||\
-      (O_NLDLY(tty))   ||\
-      (O_CRDLY(tty))   ||\
-      (O_TABDLY(tty))  ||\
-      (O_BSDLY(tty))   ||\
-      (O_VTDLY(tty))   ||\
-      (O_FFDLY(tty)))
-
-#define I_OTHER(tty)    \
-      ((I_INLCR(tty))  ||\
-      (I_IGNCR(tty))   ||\
-      (I_ICRNL(tty))   ||\
-      (I_IUCLC(tty))   ||\
-      (L_ISIG(tty)))
-
 #define SCI_MAGIC 0xbabeface
 
 /*
@@ -370,7 +356,7 @@
 		return ctrl_inb(SCPDR)&0x04 ? 1 : 0; /* IRDA */
 	return 1;
 }
-#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751)
+#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_SH4_202)
 static inline int sci_rxd_in(struct sci_port *port)
 {
 #ifndef SCIF_ONLY
diff -urN linux-2.4.23-pre5/drivers/net/Config.in linux-2.4.23-pre6/drivers/net/Config.in
--- linux-2.4.23-pre5/drivers/net/Config.in	2003-10-01 10:38:42.000000000 -0700
+++ linux-2.4.23-pre6/drivers/net/Config.in	2003-10-01 10:38:50.000000000 -0700
@@ -269,17 +269,6 @@
 dep_tristate 'Packet Engines Yellowfin Gigabit-NIC support (EXPERIMENTAL)' CONFIG_YELLOWFIN $CONFIG_PCI $CONFIG_EXPERIMENTAL
 dep_tristate 'Realtek 8169 Gigabit Ethernet support' CONFIG_R8169 $CONFIG_PCI
 dep_tristate 'Marvell Yukon Chipset / SysKonnect SK-98xx Support' CONFIG_SK98LIN $CONFIG_PCI
-if [ "$CONFIG_SK98LIN" != "n" ]; then
-   bool '  3Com 3C940/3C941 Gigabit Ethernet Adapter' CONFIG_SK98LIN_T1
-   bool '  Allied Telesyn AT-29xx Gigabit Ethernet Adapter' CONFIG_SK98LIN_T3
-   bool '  CNet N-Way Gigabit Ethernet Adapter' CONFIG_SK98LIN_T8
-   bool '  D-Link DGE-530T Gigabit Ethernet Adapter' CONFIG_SK98LIN_T6
-   bool '  Linksys EG10xx Ethernet Server Adapter' CONFIG_SK98LIN_T9
-   bool '  Marvell RDK-80xx Adapter' CONFIG_SK98LIN_T4
-   bool '  Marvell Yukon Gigabit Ethernet Adapter' CONFIG_SK98LIN_T7
-   bool '  SysKonnect SK-98xx Server Gigabit Adapter' CONFIG_SK98LIN_T2
-   bool '  SysKonnect SK-98xx V2.0 Gigabit Ethernet Adapter' CONFIG_SK98LIN_T5
-fi
 dep_tristate 'Broadcom Tigon3 support' CONFIG_TIGON3 $CONFIG_PCI
 
 if [ "$CONFIG_MOMENCO_OCELOT_C" = "y" -o "$CONFIG_MOMENCO_JAGUAR_ATX" = "y" ]; then
diff -urN linux-2.4.23-pre5/drivers/net/e1000/e1000.h linux-2.4.23-pre6/drivers/net/e1000/e1000.h
--- linux-2.4.23-pre5/drivers/net/e1000/e1000.h	2003-08-25 04:44:42.000000000 -0700
+++ linux-2.4.23-pre6/drivers/net/e1000/e1000.h	2003-10-01 10:38:50.000000000 -0700
@@ -122,7 +122,12 @@
 #define E1000_RX_BUFFER_WRITE	16	/* Must be power of 2 */
 
 #define AUTO_ALL_MODES       0
-#define E1000_EEPROM_APME    4
+#define E1000_EEPROM_APME    0x0400
+
+#ifndef E1000_MASTER_SLAVE
+/* Switch to override PHY master/slave setting */
+#define E1000_MASTER_SLAVE	e1000_ms_hw_default
+#endif
 
 /* only works for sizes that are powers of 2 */
 #define E1000_ROUNDUP(i, size) ((i) = (((i) + (size) - 1) & ~((size) - 1)))
@@ -180,6 +185,7 @@
 	spinlock_t stats_lock;
 	atomic_t irq_sem;
 	struct tq_struct tx_timeout_task;
+     	uint8_t fc_autoneg;
 
 	struct timer_list blink_timer;
 	unsigned long led_status;
@@ -194,6 +200,7 @@
 	uint32_t tx_head_addr;
 	uint32_t tx_fifo_size;
 	atomic_t tx_fifo_stall;
+	boolean_t pcix_82544;
 
 	/* RX */
 	struct e1000_desc_ring rx_ring;
diff -urN linux-2.4.23-pre5/drivers/net/e1000/e1000_ethtool.c linux-2.4.23-pre6/drivers/net/e1000/e1000_ethtool.c
--- linux-2.4.23-pre5/drivers/net/e1000/e1000_ethtool.c	2003-08-25 04:44:42.000000000 -0700
+++ linux-2.4.23-pre6/drivers/net/e1000/e1000_ethtool.c	2003-10-01 10:38:50.000000000 -0700
@@ -190,6 +190,55 @@
 	return 0;
 }
 
+static int
+e1000_ethtool_gpause(struct e1000_adapter *adapter,
+                     struct ethtool_pauseparam *epause)
+{
+	struct e1000_hw *hw = &adapter->hw;
+	
+	epause->autoneg = 
+		(adapter->fc_autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE);
+	
+	if(hw->fc == e1000_fc_rx_pause)
+		epause->rx_pause = 1;
+	else if(hw->fc == e1000_fc_tx_pause)
+		epause->tx_pause = 1;
+	else if(hw->fc == e1000_fc_full) {
+		epause->rx_pause = 1;
+		epause->tx_pause = 1;
+	}
+	
+	return 0;
+}
+
+static int
+e1000_ethtool_spause(struct e1000_adapter *adapter,
+                     struct ethtool_pauseparam *epause)
+{
+	struct e1000_hw *hw = &adapter->hw;
+	
+	adapter->fc_autoneg = epause->autoneg;
+
+	if(epause->rx_pause && epause->tx_pause)
+		hw->fc = e1000_fc_full;
+	else if(epause->rx_pause && !epause->tx_pause)
+		hw->fc = e1000_fc_rx_pause;
+	else if(!epause->rx_pause && epause->tx_pause)
+		hw->fc = e1000_fc_tx_pause;
+	else if(!epause->rx_pause && !epause->tx_pause)
+		hw->fc = e1000_fc_none;
+
+	hw->original_fc = hw->fc;
+
+	if(netif_running(adapter->netdev)) {
+		e1000_down(adapter);
+		e1000_up(adapter);
+	} else
+		e1000_reset(adapter);
+	
+	return 0;
+}
+
 static void
 e1000_ethtool_gdrvinfo(struct e1000_adapter *adapter,
                        struct ethtool_drvinfo *drvinfo)
@@ -197,7 +246,7 @@
 	strncpy(drvinfo->driver,  e1000_driver_name, 32);
 	strncpy(drvinfo->version, e1000_driver_version, 32);
 	strncpy(drvinfo->fw_version, "N/A", 32);
-	strncpy(drvinfo->bus_info, adapter->pdev->slot_name, 32);
+	strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32);
 	drvinfo->n_stats = E1000_STATS_LEN;
 	drvinfo->testinfo_len = E1000_TEST_LEN;
 #define E1000_REGS_LEN 32
@@ -504,7 +553,7 @@
 	return *data;
 }
 
-static void
+static irqreturn_t
 e1000_test_intr(int irq,
 		void *data,
 		struct pt_regs *regs)
@@ -514,7 +563,7 @@
 
 	adapter->test_icr |= E1000_READ_REG(&adapter->hw, ICR);
 
-	return;
+	return IRQ_HANDLED;
 }
 
 static int
@@ -958,9 +1007,13 @@
 	case e1000_82544:
 	case e1000_82540:
 	case e1000_82545:
+	case e1000_82545_rev_3:
 	case e1000_82546:
+	case e1000_82546_rev_3:
 	case e1000_82541:
+	case e1000_82541_rev_2:
 	case e1000_82547:
+	case e1000_82547_rev_2:
 		return e1000_integrated_phy_loopback(adapter);
 		break;
 
@@ -983,9 +1036,12 @@
 {
 	uint32_t rctl;
 
-	if(adapter->hw.media_type == e1000_media_type_fiber) {
+	if(adapter->hw.media_type == e1000_media_type_fiber ||
+	   adapter->hw.media_type == e1000_media_type_internal_serdes) {
 		if(adapter->hw.mac_type == e1000_82545 ||
-		   adapter->hw.mac_type == e1000_82546)
+		   adapter->hw.mac_type == e1000_82546 ||
+		   adapter->hw.mac_type == e1000_82545_rev_3 ||
+		   adapter->hw.mac_type == e1000_82546_rev_3)
 			return e1000_set_phy_loopback(adapter);
 		else {
 			rctl = E1000_READ_REG(&adapter->hw, RCTL);
@@ -1010,9 +1066,12 @@
 	E1000_WRITE_REG(&adapter->hw, RCTL, rctl);
 
 	if(adapter->hw.media_type == e1000_media_type_copper ||
-	   (adapter->hw.media_type == e1000_media_type_fiber &&
+	   ((adapter->hw.media_type == e1000_media_type_fiber ||
+	     adapter->hw.media_type == e1000_media_type_internal_serdes) &&
 	    (adapter->hw.mac_type == e1000_82545 ||
-	     adapter->hw.mac_type == e1000_82546))) {
+	     adapter->hw.mac_type == e1000_82546 ||
+	     adapter->hw.mac_type == e1000_82545_rev_3 ||
+	     adapter->hw.mac_type == e1000_82546_rev_3))) {
 		adapter->hw.autoneg = TRUE;
 		e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &phy_reg);
 		if(phy_reg & MII_CR_LOOPBACK) {
@@ -1114,7 +1173,7 @@
 			e1000_down(adapter);
 		else
 			e1000_reset(adapter);
-		
+
 		if(e1000_reg_test(adapter, &data[0]))
 			eth_test->flags |= ETH_TEST_FL_FAILED;
 
@@ -1162,6 +1221,7 @@
 		return;
 
 	case E1000_DEV_ID_82546EB_FIBER:
+	case E1000_DEV_ID_82546GB_FIBER:
 		/* Wake events only supported on port A for dual fiber */
 		if(E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1) {
 			wol->supported = 0;
@@ -1200,6 +1260,7 @@
 		return wol->wolopts ? -EOPNOTSUPP : 0;
 
 	case E1000_DEV_ID_82546EB_FIBER:
+	case E1000_DEV_ID_82546GB_FIBER:
 		/* Wake events only supported on port A for dual fiber */
 		if(E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)
 			return wol->wolopts ? -EOPNOTSUPP : 0;
@@ -1437,6 +1498,19 @@
 		addr += offsetof(struct ethtool_eeprom, data);
 		return e1000_ethtool_seeprom(adapter, &eeprom, addr);
 	}
+	case ETHTOOL_GPAUSEPARAM: {
+		struct ethtool_pauseparam epause = {ETHTOOL_GPAUSEPARAM};
+		e1000_ethtool_gpause(adapter, &epause);
+		if(copy_to_user(addr, &epause, sizeof(epause)))
+			return -EFAULT;
+		return 0;
+	}
+	case ETHTOOL_SPAUSEPARAM: {
+		struct ethtool_pauseparam epause;
+		if(copy_from_user(&epause, addr, sizeof(epause)))
+			return -EFAULT;
+		return e1000_ethtool_spause(adapter, &epause);
+	}
 	case ETHTOOL_GSTATS: {
 		struct {
 			struct ethtool_stats eth_stats;
@@ -1475,6 +1549,107 @@
 			return -EFAULT;
 		return 0;
 	}
+	case ETHTOOL_GRXCSUM: {
+		struct ethtool_value edata = { ETHTOOL_GRXCSUM };
+
+		edata.data = adapter->rx_csum;
+		if (copy_to_user(addr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	case ETHTOOL_SRXCSUM: {
+		struct ethtool_value edata;
+
+		if (copy_from_user(&edata, addr, sizeof(edata)))
+			return -EFAULT;
+		adapter->rx_csum = edata.data;
+		if(netif_running(netdev)) {
+			e1000_down(adapter);
+			e1000_up(adapter);
+		} else
+			e1000_reset(adapter);
+		return 0;
+	}
+	case ETHTOOL_GTXCSUM: {
+		struct ethtool_value edata = { ETHTOOL_GTXCSUM };
+
+		edata.data =
+			(netdev->features & NETIF_F_HW_CSUM) != 0;
+		if (copy_to_user(addr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	case ETHTOOL_STXCSUM: {
+		struct ethtool_value edata;
+
+		if (copy_from_user(&edata, addr, sizeof(edata)))
+			return -EFAULT;
+
+		if(adapter->hw.mac_type < e1000_82543) {
+			if (edata.data != 0)
+				return -EINVAL;
+			return 0;
+		}
+
+		if (edata.data)
+			netdev->features |= NETIF_F_HW_CSUM;
+		else
+			netdev->features &= ~NETIF_F_HW_CSUM;
+
+		return 0;
+	}
+	case ETHTOOL_GSG: {
+		struct ethtool_value edata = { ETHTOOL_GSG };
+
+		edata.data =
+			(netdev->features & NETIF_F_SG) != 0;
+		if (copy_to_user(addr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	case ETHTOOL_SSG: {
+		struct ethtool_value edata;
+
+		if (copy_from_user(&edata, addr, sizeof(edata)))
+			return -EFAULT;
+
+		if (edata.data)
+			netdev->features |= NETIF_F_SG;
+		else
+			netdev->features &= ~NETIF_F_SG;
+
+		return 0;
+	}
+#ifdef NETIF_F_TSO
+	case ETHTOOL_GTSO: {
+		struct ethtool_value edata = { ETHTOOL_GTSO };
+
+		edata.data = (netdev->features & NETIF_F_TSO) != 0;
+		if (copy_to_user(addr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	case ETHTOOL_STSO: {
+		struct ethtool_value edata;
+
+		if (copy_from_user(&edata, addr, sizeof(edata)))
+			return -EFAULT;
+
+		if ((adapter->hw.mac_type < e1000_82544) ||
+		    (adapter->hw.mac_type == e1000_82547)) {
+			if (edata.data != 0)
+				return -EINVAL;
+			return 0;
+		}
+
+		if (edata.data)
+			netdev->features |= NETIF_F_TSO;
+		else
+			netdev->features &= ~NETIF_F_TSO;
+
+		return 0;
+	}
+#endif
 	default:
 		return -EOPNOTSUPP;
 	}
diff -urN linux-2.4.23-pre5/drivers/net/e1000/e1000_hw.c linux-2.4.23-pre6/drivers/net/e1000/e1000_hw.c
--- linux-2.4.23-pre5/drivers/net/e1000/e1000_hw.c	2003-08-25 04:44:42.000000000 -0700
+++ linux-2.4.23-pre6/drivers/net/e1000/e1000_hw.c	2003-10-01 10:38:50.000000000 -0700
@@ -34,14 +34,15 @@
 
 static int32_t e1000_set_phy_type(struct e1000_hw *hw);
 static void e1000_phy_init_script(struct e1000_hw *hw);
-static int32_t e1000_setup_fiber_link(struct e1000_hw *hw);
 static int32_t e1000_setup_copper_link(struct e1000_hw *hw);
+static int32_t e1000_setup_fiber_serdes_link(struct e1000_hw *hw);
+static int32_t e1000_adjust_serdes_amplitude(struct e1000_hw *hw);
 static int32_t e1000_phy_force_speed_duplex(struct e1000_hw *hw);
 static int32_t e1000_config_mac_to_phy(struct e1000_hw *hw);
-static int32_t e1000_force_mac_fc(struct e1000_hw *hw);
 static void e1000_raise_mdi_clk(struct e1000_hw *hw, uint32_t *ctrl);
 static void e1000_lower_mdi_clk(struct e1000_hw *hw, uint32_t *ctrl);
-static void e1000_shift_out_mdi_bits(struct e1000_hw *hw, uint32_t data, uint16_t count);
+static void e1000_shift_out_mdi_bits(struct e1000_hw *hw, uint32_t data,
+                                     uint16_t count);
 static uint16_t e1000_shift_in_mdi_bits(struct e1000_hw *hw);
 static int32_t e1000_phy_reset_dsp(struct e1000_hw *hw);
 static int32_t e1000_write_eeprom_spi(struct e1000_hw *hw, uint16_t offset,
@@ -52,14 +53,31 @@
 static int32_t e1000_spi_eeprom_ready(struct e1000_hw *hw);
 static void e1000_raise_ee_clk(struct e1000_hw *hw, uint32_t *eecd);
 static void e1000_lower_ee_clk(struct e1000_hw *hw, uint32_t *eecd);
-static void e1000_shift_out_ee_bits(struct e1000_hw *hw, uint16_t data, uint16_t count);
+static void e1000_shift_out_ee_bits(struct e1000_hw *hw, uint16_t data,
+                                    uint16_t count);
+static int32_t e1000_write_phy_reg_ex(struct e1000_hw *hw, uint32_t reg_addr,
+                                      uint16_t phy_data);
+static int32_t e1000_read_phy_reg_ex(struct e1000_hw *hw,uint32_t reg_addr,
+                                     uint16_t *phy_data);
 static uint16_t e1000_shift_in_ee_bits(struct e1000_hw *hw, uint16_t count);
 static int32_t e1000_acquire_eeprom(struct e1000_hw *hw);
 static void e1000_release_eeprom(struct e1000_hw *hw);
 static void e1000_standby_eeprom(struct e1000_hw *hw);
 static int32_t e1000_id_led_init(struct e1000_hw * hw);
+static int32_t e1000_set_vco_speed(struct e1000_hw *hw);
 
 
+/* IGP cable length table */
+static const
+uint16_t e1000_igp_cable_length_table[IGP01E1000_AGC_LENGTH_TABLE_SIZE] =
+    { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+      5, 10, 10, 10, 10, 10, 10, 10, 20, 20, 20, 20, 20, 25, 25, 25,
+      25, 25, 25, 25, 30, 30, 30, 30, 40, 40, 40, 40, 40, 40, 40, 40,
+      40, 50, 50, 50, 50, 50, 50, 50, 60, 60, 60, 60, 60, 60, 60, 60,
+      60, 70, 70, 70, 70, 70, 70, 80, 80, 80, 80, 80, 80, 90, 90, 90,
+      90, 90, 90, 90, 90, 90, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
+      100, 100, 100, 100, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110,
+      110, 110, 110, 110, 110, 110, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120};
 
 /******************************************************************************
  * Set the phy type member in the hw struct.
@@ -100,50 +118,41 @@
     DEBUGFUNC("e1000_phy_init_script");
 
     if(hw->phy_init_script) {
-        msec_delay(10);
+        msec_delay(20);
 
-        e1000_write_phy_reg(hw,IGP01E1000_PHY_PAGE_SELECT,0x0000);
         e1000_write_phy_reg(hw,0x0000,0x0140);
 
         msec_delay(5);
-        e1000_write_phy_reg(hw,IGP01E1000_PHY_PAGE_SELECT,0x1F95);
-        e1000_write_phy_reg(hw,0x0015,0x0001);
 
-        e1000_write_phy_reg(hw,IGP01E1000_PHY_PAGE_SELECT,0x1F71);
-        e1000_write_phy_reg(hw,0x0011,0xBD21);
+        if(hw->mac_type == e1000_82541 || hw->mac_type == e1000_82547) {
+            e1000_write_phy_reg(hw, 0x1F95, 0x0001);
+
+            e1000_write_phy_reg(hw, 0x1F71, 0xBD21);
 
-        e1000_write_phy_reg(hw,IGP01E1000_PHY_PAGE_SELECT,0x1F79);
-        e1000_write_phy_reg(hw,0x0019,0x0018);
+            e1000_write_phy_reg(hw, 0x1F79, 0x0018);
 
-        e1000_write_phy_reg(hw,IGP01E1000_PHY_PAGE_SELECT,0x1F30);
-        e1000_write_phy_reg(hw,0x0010,0x1600);
+            e1000_write_phy_reg(hw, 0x1F30, 0x1600);
 
-        e1000_write_phy_reg(hw,IGP01E1000_PHY_PAGE_SELECT,0x1F31);
-        e1000_write_phy_reg(hw,0x0011,0x0014);
+            e1000_write_phy_reg(hw, 0x1F31, 0x0014);
 
-        e1000_write_phy_reg(hw,IGP01E1000_PHY_PAGE_SELECT,0x1F32);
-        e1000_write_phy_reg(hw,0x0012,0x161C);
+            e1000_write_phy_reg(hw, 0x1F32, 0x161C);
 
-        e1000_write_phy_reg(hw,IGP01E1000_PHY_PAGE_SELECT,0x1F94);
-        e1000_write_phy_reg(hw,0x0014,0x0003);
+            e1000_write_phy_reg(hw, 0x1F94, 0x0003);
 
-        e1000_write_phy_reg(hw,IGP01E1000_PHY_PAGE_SELECT,0x1F96);
-        e1000_write_phy_reg(hw,0x0016,0x003F);
+            e1000_write_phy_reg(hw, 0x1F96, 0x003F);
 
-        e1000_write_phy_reg(hw,IGP01E1000_PHY_PAGE_SELECT,0x2010);
-        e1000_write_phy_reg(hw,0x0010,0x0008);
+            e1000_write_phy_reg(hw, 0x2010, 0x0008);
+        } else {
+            e1000_write_phy_reg(hw, 0x1F73, 0x0099);
+        }
 
-        e1000_write_phy_reg(hw,IGP01E1000_PHY_PAGE_SELECT,0x0000);
-        e1000_write_phy_reg(hw,0x0000,0x3300);
+        e1000_write_phy_reg(hw, 0x0000, 0x3300);
 
 
         if(hw->mac_type == e1000_82547) {
             uint16_t fused, fine, coarse;
 
             /* Move to analog registers page */
-            e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT, 
-                                IGP01E1000_ANALOG_REGS_PAGE);
-
             e1000_read_phy_reg(hw, IGP01E1000_ANALOG_SPARE_FUSE_STATUS, &fused);
 
             if(!(fused & IGP01E1000_ANALOG_SPARE_FUSE_ENABLED)) {
@@ -158,17 +167,14 @@
                 } else if(coarse == IGP01E1000_ANALOG_FUSE_COARSE_THRESH)
                     fine -= IGP01E1000_ANALOG_FUSE_FINE_10;
 
-                fused = (fused & IGP01E1000_ANALOG_FUSE_POLY_MASK) | 
-                        (fine & IGP01E1000_ANALOG_FUSE_FINE_MASK) | 
+                fused = (fused & IGP01E1000_ANALOG_FUSE_POLY_MASK) |
+                        (fine & IGP01E1000_ANALOG_FUSE_FINE_MASK) |
                         (coarse & IGP01E1000_ANALOG_FUSE_COARSE_MASK);
 
                 e1000_write_phy_reg(hw, IGP01E1000_ANALOG_FUSE_CONTROL, fused);
-                e1000_write_phy_reg(hw, IGP01E1000_ANALOG_FUSE_BYPASS, 
+                e1000_write_phy_reg(hw, IGP01E1000_ANALOG_FUSE_BYPASS,
                                     IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL);
             }
-            /* Return to first page of registers */
-            e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT,
-                                IGP01E1000_IEEE_REGS_PAGE);
         }
     }
 }
@@ -218,32 +224,89 @@
     case E1000_DEV_ID_82545EM_FIBER:
         hw->mac_type = e1000_82545;
         break;
+    case E1000_DEV_ID_82545GM_COPPER:
+    case E1000_DEV_ID_82545GM_FIBER:
+    case E1000_DEV_ID_82545GM_SERDES:
+        hw->mac_type = e1000_82545_rev_3;
+        break;
     case E1000_DEV_ID_82546EB_COPPER:
     case E1000_DEV_ID_82546EB_FIBER:
     case E1000_DEV_ID_82546EB_QUAD_COPPER:
         hw->mac_type = e1000_82546;
         break;
+    case E1000_DEV_ID_82546GB_COPPER:
+    case E1000_DEV_ID_82546GB_FIBER:
+    case E1000_DEV_ID_82546GB_SERDES:
+        hw->mac_type = e1000_82546_rev_3;
+        break;
     case E1000_DEV_ID_82541EI:
-    case E1000_DEV_ID_82541EP:
+    case E1000_DEV_ID_82541EI_MOBILE:
         hw->mac_type = e1000_82541;
         break;
+    case E1000_DEV_ID_82541ER:
+    case E1000_DEV_ID_82541GI:
+    case E1000_DEV_ID_82541GI_MOBILE:
+        hw->mac_type = e1000_82541_rev_2;
+        break;
     case E1000_DEV_ID_82547EI:
         hw->mac_type = e1000_82547;
         break;
+    case E1000_DEV_ID_82547GI:
+        hw->mac_type = e1000_82547_rev_2;
+        break;
     default:
         /* Should never have loaded on this device */
         return -E1000_ERR_MAC_TYPE;
     }
 
-
     return E1000_SUCCESS;
 }
+
+/*****************************************************************************
+ * Set media type and TBI compatibility.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * **************************************************************************/
+void
+e1000_set_media_type(struct e1000_hw *hw)
+{
+    uint32_t status;
+
+    DEBUGFUNC("e1000_set_media_type");
+
+    if(hw->mac_type != e1000_82543) {
+        /* tbi_compatibility is only valid on 82543 */
+        hw->tbi_compatibility_en = FALSE;
+    }
+
+    switch (hw->device_id) {
+    case E1000_DEV_ID_82545GM_SERDES:
+    case E1000_DEV_ID_82546GB_SERDES:
+        hw->media_type = e1000_media_type_internal_serdes;
+        break;
+    default:
+        if(hw->mac_type >= e1000_82543) {
+            status = E1000_READ_REG(hw, STATUS);
+            if(status & E1000_STATUS_TBIMODE) {
+                hw->media_type = e1000_media_type_fiber;
+                /* tbi_compatibility not valid on fiber */
+                hw->tbi_compatibility_en = FALSE;
+            } else {
+                hw->media_type = e1000_media_type_copper;
+            }
+        } else {
+            /* This is an 82542 (fiber only) */
+            hw->media_type = e1000_media_type_fiber;
+        }
+    }
+}
+
 /******************************************************************************
  * Reset the transmit and receive units; mask and clear all interrupts.
  *
  * hw - Struct containing variables accessed by shared code
  *****************************************************************************/
-void
+int32_t
 e1000_reset_hw(struct e1000_hw *hw)
 {
     uint32_t ctrl;
@@ -280,49 +343,75 @@
      */
     msec_delay(10);
 
-    /* Issue a global reset to the MAC.  This will reset the chip's
-     * transmit, receive, DMA, and link units.  It will not effect
-     * the current PCI configuration.  The global reset bit is self-
-     * clearing, and should clear within a microsecond.
-     */
-    DEBUGOUT("Issuing a global reset to MAC\n");
     ctrl = E1000_READ_REG(hw, CTRL);
 
     /* Must reset the PHY before resetting the MAC */
     if((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) {
         E1000_WRITE_REG_IO(hw, CTRL, (ctrl | E1000_CTRL_PHY_RST));
-	msec_delay(5);
+        msec_delay(5);
     }
 
+    /* Issue a global reset to the MAC.  This will reset the chip's
+     * transmit, receive, DMA, and link units.  It will not effect
+     * the current PCI configuration.  The global reset bit is self-
+     * clearing, and should clear within a microsecond.
+     */
+    DEBUGOUT("Issuing a global reset to MAC\n");
+
     switch(hw->mac_type) {
         case e1000_82544:
         case e1000_82540:
         case e1000_82545:
         case e1000_82546:
         case e1000_82541:
+        case e1000_82541_rev_2:
             /* These controllers can't ack the 64-bit write when issuing the
              * reset, so use IO-mapping as a workaround to issue the reset */
             E1000_WRITE_REG_IO(hw, CTRL, (ctrl | E1000_CTRL_RST));
             break;
+        case e1000_82545_rev_3:
+        case e1000_82546_rev_3:
+            /* Reset is performed on a shadow of the control register */
+            E1000_WRITE_REG(hw, CTRL_DUP, (ctrl | E1000_CTRL_RST));
+            break;
         default:
             E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_RST));
             break;
     }
 
-    /* Force a reload from the EEPROM if necessary */
-    if(hw->mac_type < e1000_82540) {
-        /* Wait for reset to complete */
-        udelay(10);
-        ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
-        ctrl_ext |= E1000_CTRL_EXT_EE_RST;
-        E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
-        E1000_WRITE_FLUSH(hw);
-        /* Wait for EEPROM reload */
-        msec_delay(2);
-    } else {
-        /* Wait for EEPROM reload (it happens automatically) */
-        msec_delay(5);
-        /* Dissable HW ARPs on ASF enabled adapters */
+    /* After MAC reset, force reload of EEPROM to restore power-on settings to
+     * device.  Later controllers reload the EEPROM automatically, so just wait
+     * for reload to complete.
+     */
+    switch(hw->mac_type) {
+        case e1000_82542_rev2_0:
+        case e1000_82542_rev2_1:
+        case e1000_82543:
+        case e1000_82544:
+            /* Wait for reset to complete */
+            udelay(10);
+            ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
+            ctrl_ext |= E1000_CTRL_EXT_EE_RST;
+            E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
+            E1000_WRITE_FLUSH(hw);
+            /* Wait for EEPROM reload */
+            msec_delay(2);
+            break;
+        case e1000_82541:
+        case e1000_82541_rev_2:
+        case e1000_82547:
+        case e1000_82547_rev_2:
+            /* Wait for EEPROM reload */
+            msec_delay(20);
+            break;
+        default:
+            /* Wait for EEPROM reload (it happens automatically) */
+            msec_delay(5);
+            break;
+    }
+
+    /* Disable HW ARPs on ASF enabled adapters */
+    if(hw->mac_type >= e1000_82540) {
         manc = E1000_READ_REG(hw, MANC);
         manc &= ~(E1000_MANC_ARP_EN);
         E1000_WRITE_REG(hw, MANC, manc);
@@ -350,6 +439,8 @@
         if(hw->pci_cmd_word & CMD_MEM_WRT_INVALIDATE)
             e1000_pci_set_mwi(hw);
     }
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -366,7 +457,7 @@
 int32_t
 e1000_init_hw(struct e1000_hw *hw)
 {
-    uint32_t ctrl, status;
+    uint32_t ctrl;
     uint32_t i;
     int32_t ret_val;
     uint16_t pcix_cmd_word;
@@ -377,31 +468,13 @@
     DEBUGFUNC("e1000_init_hw");
 
     /* Initialize Identification LED */
-    ret_val = e1000_id_led_init(hw);
-    if(ret_val < 0) {
+    if((ret_val = e1000_id_led_init(hw))) {
         DEBUGOUT("Error Initializing Identification LED\n");
         return ret_val;
     }
 
-    /* Set the Media Type and exit with error if it is not valid. */
-    if(hw->mac_type != e1000_82543) {
-        /* tbi_compatibility is only valid on 82543 */
-        hw->tbi_compatibility_en = FALSE;
-    }
-
-    if(hw->mac_type >= e1000_82543) {
-        status = E1000_READ_REG(hw, STATUS);
-        if(status & E1000_STATUS_TBIMODE) {
-            hw->media_type = e1000_media_type_fiber;
-            /* tbi_compatibility not valid on fiber */
-            hw->tbi_compatibility_en = FALSE;
-        } else {
-            hw->media_type = e1000_media_type_copper;
-        }
-    } else {
-        /* This is an 82542 (fiber only) */
-        hw->media_type = e1000_media_type_fiber;
-    }
+    /* Set the media type and TBI compatibility */
+    e1000_set_media_type(hw);
 
     /* Disabling VLAN filtering. */
     DEBUGOUT("Initializing the IEEE VLAN\n");
@@ -446,21 +519,30 @@
         E1000_WRITE_REG(hw, CTRL, ctrl | E1000_CTRL_PRIOR);
     }
 
-    /* Workaround for PCI-X problem when BIOS sets MMRBC incorrectly. */
-    if(hw->bus_type == e1000_bus_type_pcix) {
-        e1000_read_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd_word);
-        e1000_read_pci_cfg(hw, PCIX_STATUS_REGISTER_HI, &pcix_stat_hi_word);
-        cmd_mmrbc = (pcix_cmd_word & PCIX_COMMAND_MMRBC_MASK) >>
-            PCIX_COMMAND_MMRBC_SHIFT;
-        stat_mmrbc = (pcix_stat_hi_word & PCIX_STATUS_HI_MMRBC_MASK) >>
-            PCIX_STATUS_HI_MMRBC_SHIFT;
-        if(stat_mmrbc == PCIX_STATUS_HI_MMRBC_4K)
-            stat_mmrbc = PCIX_STATUS_HI_MMRBC_2K;
-        if(cmd_mmrbc > stat_mmrbc) {
-            pcix_cmd_word &= ~PCIX_COMMAND_MMRBC_MASK;
-            pcix_cmd_word |= stat_mmrbc << PCIX_COMMAND_MMRBC_SHIFT;
-            e1000_write_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd_word);
+    switch(hw->mac_type) {
+    case e1000_82545_rev_3:
+    case e1000_82546_rev_3:
+        break;
+    default:
+        /* Workaround for PCI-X problem when BIOS sets MMRBC incorrectly. */
+        if(hw->bus_type == e1000_bus_type_pcix) {
+            e1000_read_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd_word);
+            e1000_read_pci_cfg(hw, PCIX_STATUS_REGISTER_HI,
+                &pcix_stat_hi_word);
+            cmd_mmrbc = (pcix_cmd_word & PCIX_COMMAND_MMRBC_MASK) >>
+                PCIX_COMMAND_MMRBC_SHIFT;
+            stat_mmrbc = (pcix_stat_hi_word & PCIX_STATUS_HI_MMRBC_MASK) >>
+                PCIX_STATUS_HI_MMRBC_SHIFT;
+            if(stat_mmrbc == PCIX_STATUS_HI_MMRBC_4K)
+                stat_mmrbc = PCIX_STATUS_HI_MMRBC_2K;
+            if(cmd_mmrbc > stat_mmrbc) {
+                pcix_cmd_word &= ~PCIX_COMMAND_MMRBC_MASK;
+                pcix_cmd_word |= stat_mmrbc << PCIX_COMMAND_MMRBC_SHIFT;
+                e1000_write_pci_cfg(hw, PCIX_COMMAND_REGISTER,
+                    &pcix_cmd_word);
+            }
         }
+        break;
     }
 
     /* Call a subroutine to configure the link and setup flow control. */
@@ -484,6 +566,46 @@
 }
 
 /******************************************************************************
+ * Adjust SERDES output amplitude based on EEPROM setting.
+ *
+ * hw - Struct containing variables accessed by shared code.
+ *****************************************************************************/
+static int32_t
+e1000_adjust_serdes_amplitude(struct e1000_hw *hw)
+{
+    uint16_t eeprom_data;
+    int32_t  ret_val;
+
+    DEBUGFUNC("e1000_adjust_serdes_amplitude");
+
+    if(hw->media_type != e1000_media_type_internal_serdes)
+        return E1000_SUCCESS;
+
+    switch(hw->mac_type) {
+    case e1000_82545_rev_3:
+    case e1000_82546_rev_3:
+        break;
+    default:
+        return E1000_SUCCESS;
+    }
+
+    if ((ret_val = e1000_read_eeprom(hw, EEPROM_SERDES_AMPLITUDE, 1,
+                                     &eeprom_data))) {
+        return ret_val;
+    }
+
+    if(eeprom_data != EEPROM_RESERVED_WORD) {
+        /* Adjust SERDES output amplitude only. */
+        eeprom_data &= EEPROM_SERDES_AMPLITUDE_MASK; 
+        if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_EXT_CTRL,
+                                          eeprom_data)))
+            return ret_val;
+    }
+
+    return E1000_SUCCESS;
+}
+
+/******************************************************************************
  * Configures flow control and link settings.
  *
  * hw - Struct containing variables accessed by shared code
@@ -554,9 +676,9 @@
     }
 
     /* Call the necessary subroutine to configure the link. */
-    ret_val = (hw->media_type == e1000_media_type_fiber) ?
-              e1000_setup_fiber_link(hw) :
-              e1000_setup_copper_link(hw);
+    ret_val = (hw->media_type == e1000_media_type_copper) ?
+              e1000_setup_copper_link(hw) :
+              e1000_setup_fiber_serdes_link(hw);
 
     /* Initialize the flow control address, type, and PAUSE timer
      * registers to their default values.  This is done even if flow
@@ -595,7 +717,7 @@
 }
 
 /******************************************************************************
- * Sets up link for a fiber based adapter
+ * Sets up link for a fiber based or serdes based adapter
  *
  * hw - Struct containing variables accessed by shared code
  *
@@ -604,28 +726,37 @@
  * and receiver are not enabled.
  *****************************************************************************/
 static int32_t
-e1000_setup_fiber_link(struct e1000_hw *hw)
+e1000_setup_fiber_serdes_link(struct e1000_hw *hw)
 {
     uint32_t ctrl;
     uint32_t status;
     uint32_t txcw = 0;
     uint32_t i;
-    uint32_t signal;
+    uint32_t signal = 0;
     int32_t ret_val;
 
-    DEBUGFUNC("e1000_setup_fiber_link");
+    DEBUGFUNC("e1000_setup_fiber_serdes_link");
 
-    /* On adapters with a MAC newer that 82544, SW Defineable pin 1 will be
+    /* On adapters with a MAC newer than 82544, SW Defineable pin 1 will be
      * set when the optics detect a signal. On older adapters, it will be
-     * cleared when there is a signal
+     * cleared when there is a signal.  This applies to fiber media only.
+     * If we're on serdes media, adjust the output amplitude to value set in
+     * the EEPROM.
      */
     ctrl = E1000_READ_REG(hw, CTRL);
-    if(hw->mac_type > e1000_82544) signal = E1000_CTRL_SWDPIN1;
-    else signal = 0;
+    if(hw->media_type == e1000_media_type_fiber)
+        signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0;
+
+    if((ret_val = e1000_adjust_serdes_amplitude(hw)))
+        return ret_val;
 
     /* Take the link out of reset */
     ctrl &= ~(E1000_CTRL_LRST);
 
+    /* Adjust VCO speed to improve BER performance */
+    if((ret_val = e1000_set_vco_speed(hw)))
+        return ret_val;
+
     e1000_config_collision_dist(hw);
 
     /* Check for a software override of the flow control settings, and setup
@@ -692,8 +823,10 @@
      * indication in the Device Status Register.  Time-out if a link isn't
      * seen in 500 milliseconds seconds (Auto-negotiation should complete in
      * less than 500 milliseconds even if the other end is doing it in SW).
+     * For internal serdes, we just assume a signal is present, then poll.
      */
-    if((E1000_READ_REG(hw, CTRL) & E1000_CTRL_SWDPIN1) == signal) {
+    if(hw->media_type == e1000_media_type_internal_serdes ||
+       (E1000_READ_REG(hw, CTRL) & E1000_CTRL_SWDPIN1) == signal) {
         DEBUGOUT("Looking for Link\n");
         for(i = 0; i < (LINK_UP_TIMEOUT / 10); i++) {
             msec_delay(10);
@@ -701,19 +834,20 @@
             if(status & E1000_STATUS_LU) break;
         }
         if(i == (LINK_UP_TIMEOUT / 10)) {
-            /* AutoNeg failed to achieve a link, so we'll call
-             * e1000_check_for_link. This routine will force the link up if we
-             * detect a signal. This will allow us to communicate with
-             * non-autonegotiating link partners.
-             */
             DEBUGOUT("Never got a valid link from auto-neg!!!\n");
             hw->autoneg_failed = 1;
-            ret_val = e1000_check_for_link(hw);
-            if(ret_val < 0) {
-                DEBUGOUT("Error while checking for link\n");
-                return ret_val;
+            if(hw->media_type == e1000_media_type_fiber) {
+                /* AutoNeg failed to achieve a link, so we'll call
+                 * e1000_check_for_link. This routine will force the link up if
+                 * we detect a signal. This will allow us to communicate with
+                 * non-autonegotiating link partners.
+                 */
+                if((ret_val = e1000_check_for_link(hw))) {
+                    DEBUGOUT("Error while checking for link\n");
+                    return ret_val;
+                }
+                hw->autoneg_failed = 0;
             }
-            hw->autoneg_failed = 0;
         } else {
             hw->autoneg_failed = 0;
             DEBUGOUT("Valid Link Found\n");
@@ -721,7 +855,7 @@
     } else {
         DEBUGOUT("No Signal Detected\n");
     }
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -756,233 +890,281 @@
     }
 
     /* Make sure we have a valid PHY */
-    ret_val = e1000_detect_gig_phy(hw);
-    if(ret_val < 0) {
+    if((ret_val = e1000_detect_gig_phy(hw))) {
         DEBUGOUT("Error, did not detect valid phy.\n");
         return ret_val;
     }
     DEBUGOUT1("Phy ID = %x \n", hw->phy_id);
 
-    if (hw->phy_type == e1000_phy_igp) {
-
-        ret_val = e1000_phy_reset(hw);
-        if(ret_val < 0) {
-            DEBUGOUT("Error Resetting the PHY\n");
-            return ret_val;
-        }
+    if(hw->mac_type <= e1000_82543 ||
+       hw->mac_type == e1000_82541 || hw->mac_type == e1000_82547 ||
+       hw->mac_type == e1000_82541_rev_2 || hw->mac_type == e1000_82547_rev_2)
+        hw->phy_reset_disable = FALSE;
 
-        /* Wait 10ms for MAC to configure PHY from eeprom settings */
-        msec_delay(15);
+    if(!hw->phy_reset_disable) {
+        if (hw->phy_type == e1000_phy_igp) {
 
-        if(e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT, 0x0000) < 0) {
-            DEBUGOUT("PHY Write Error\n");
-            return -E1000_ERR_PHY;
-        }
+            if((ret_val = e1000_phy_reset(hw))) {
+                DEBUGOUT("Error Resetting the PHY\n");
+                return ret_val;
+            }
 
-        /* Configure activity LED after PHY reset */
-        led_ctrl = E1000_READ_REG(hw, LEDCTL);
-        led_ctrl &= IGP_ACTIVITY_LED_MASK;
-        led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE);
-        E1000_WRITE_REG(hw, LEDCTL, led_ctrl);
+            /* Wait 10ms for MAC to configure PHY from eeprom settings */
+            msec_delay(15);
 
-        if(hw->autoneg_advertised == ADVERTISE_1000_FULL) {
-            /* Disable SmartSpeed */
-            if(e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
-                                  &phy_data) < 0) {
-                DEBUGOUT("PHY Read Error\n");
-                return -E1000_ERR_PHY;
-            }
-            phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED;
-            if(e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
-                                   phy_data) < 0) {
-                DEBUGOUT("PHY Write Error\n");
-                return -E1000_ERR_PHY;
-            }
-            /* Set auto Master/Slave resolution process */
-            if(e1000_read_phy_reg(hw, PHY_1000T_CTRL, &phy_data) < 0) {
-                DEBUGOUT("PHY Read Error\n");
-                return -E1000_ERR_PHY;
+            /* Configure activity LED after PHY reset */
+            led_ctrl = E1000_READ_REG(hw, LEDCTL);
+            led_ctrl &= IGP_ACTIVITY_LED_MASK;
+            led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE);
+            E1000_WRITE_REG(hw, LEDCTL, led_ctrl);
+
+            /* disable lplu d3 during driver init */
+            if((ret_val = e1000_set_d3_lplu_state(hw, FALSE))) {
+                DEBUGOUT("Error Disabling LPLU D3\n");
+                return ret_val;
             }
-            phy_data &= ~CR_1000T_MS_ENABLE;
-            if(e1000_write_phy_reg(hw, PHY_1000T_CTRL, phy_data) < 0) {
-                DEBUGOUT("PHY Write Error\n");
-                return -E1000_ERR_PHY;
+
+            /* Configure mdi-mdix settings */
+            if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL,
+                                             &phy_data)))
+                return ret_val;
+
+            if((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) {
+                hw->dsp_config_state = e1000_dsp_config_disabled;
+                /* Force MDI for IGP B-0 PHY */
+                phy_data &= ~(IGP01E1000_PSCR_AUTO_MDIX |
+                              IGP01E1000_PSCR_FORCE_MDI_MDIX);
+                hw->mdix = 1;
+
+            } else {
+                hw->dsp_config_state = e1000_dsp_config_enabled;
+                phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX;
+
+                switch (hw->mdix) {
+                case 1:
+                    phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX;
+                    break;
+                case 2:
+                    phy_data |= IGP01E1000_PSCR_FORCE_MDI_MDIX;
+                    break;
+                case 0:
+                default:
+                    phy_data |= IGP01E1000_PSCR_AUTO_MDIX;
+                    break;
+                }
             }
-        }
+            if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL,
+                                              phy_data)))
+                return ret_val;
 
-        if(e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
+            /* set auto-master slave resolution settings */
+            if(hw->autoneg) {
+                e1000_ms_type phy_ms_setting = hw->master_slave;
+
+                if(hw->ffe_config_state == e1000_ffe_config_active)
+                    hw->ffe_config_state = e1000_ffe_config_enabled;
+
+                if(hw->dsp_config_state == e1000_dsp_config_activated)
+                    hw->dsp_config_state = e1000_dsp_config_enabled;
+
+                /* when autonegotiation advertisment is only 1000Mbps then we
+                 * should disable SmartSpeed and enable Auto MasterSlave
+                 * resolution as hardware default. */
+                if(hw->autoneg_advertised == ADVERTISE_1000_FULL) {
+                    /* Disable SmartSpeed */
+                    if((ret_val = e1000_read_phy_reg(hw,
+                                                    IGP01E1000_PHY_PORT_CONFIG,
+                                                    &phy_data)))
+                        return ret_val;
+                    phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED;
+                    if((ret_val = e1000_write_phy_reg(hw,
+                                                     IGP01E1000_PHY_PORT_CONFIG,
+                                                     phy_data)))
+                        return ret_val;
+                    /* Set auto Master/Slave resolution process */
+                    if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL,
+                                                     &phy_data)))
+                        return ret_val;
+                    phy_data &= ~CR_1000T_MS_ENABLE;
+                    if((ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL,
+                                                      phy_data)))
+                        return ret_val;
+                }
 
-        /* Force MDI for IGP PHY */
-        phy_data &= ~(IGP01E1000_PSCR_AUTO_MDIX |
-                      IGP01E1000_PSCR_FORCE_MDI_MDIX);
+                if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL,
+                                                 &phy_data)))
+                    return ret_val;
 
-        hw->mdix = 1;
+                /* load defaults for future use */
+                hw->original_master_slave = (phy_data & CR_1000T_MS_ENABLE) ?
+                                            ((phy_data & CR_1000T_MS_VALUE) ?
+                                             e1000_ms_force_master :
+                                             e1000_ms_force_slave) :
+                                             e1000_ms_auto;
+
+                switch (phy_ms_setting) {
+                case e1000_ms_force_master:
+                    phy_data |= (CR_1000T_MS_ENABLE | CR_1000T_MS_VALUE);
+                    break;
+                case e1000_ms_force_slave:
+                    phy_data |= CR_1000T_MS_ENABLE;
+                    phy_data &= ~(CR_1000T_MS_VALUE);
+                    break;
+                case e1000_ms_auto:
+                    phy_data &= ~CR_1000T_MS_ENABLE;
+                default:
+                    break;
+                }
+                if((ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL,
+                                                  phy_data)))
+                    return ret_val;
+            }
+        } else {
+            /* Enable CRS on TX. This must be set for half-duplex operation. */
+            if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL,
+                                             &phy_data)))
+                return ret_val;
 
-        if(e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data) < 0) {
-            DEBUGOUT("PHY Write Error\n");
-            return -E1000_ERR_PHY;
-        }
+            phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
 
-    } else {
-        /* Enable CRS on TX. This must be set for half-duplex operation. */
-        if(e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
-        phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
+            /* Options:
+             *   MDI/MDI-X = 0 (default)
+             *   0 - Auto for all speeds
+             *   1 - MDI mode
+             *   2 - MDI-X mode
+             *   3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes)
+             */
+            phy_data &= ~M88E1000_PSCR_AUTO_X_MODE;
 
-        /* Options:
-         *   MDI/MDI-X = 0 (default)
-         *   0 - Auto for all speeds
-         *   1 - MDI mode
-         *   2 - MDI-X mode
-         *   3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes)
-         */
-        phy_data &= ~M88E1000_PSCR_AUTO_X_MODE;
+            switch (hw->mdix) {
+            case 1:
+                phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE;
+                break;
+            case 2:
+                phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE;
+                break;
+            case 3:
+                phy_data |= M88E1000_PSCR_AUTO_X_1000T;
+                break;
+            case 0:
+            default:
+                phy_data |= M88E1000_PSCR_AUTO_X_MODE;
+                break;
+            }
 
-        switch (hw->mdix) {
-        case 1:
-            phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE;
-            break;
-        case 2:
-            phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE;
-            break;
-        case 3:
-            phy_data |= M88E1000_PSCR_AUTO_X_1000T;
-            break;
-        case 0:
-        default:
-            phy_data |= M88E1000_PSCR_AUTO_X_MODE;
-            break;
-        }
+            /* Options:
+             *   disable_polarity_correction = 0 (default)
+             *       Automatic Correction for Reversed Cable Polarity
+             *   0 - Disabled
+             *   1 - Enabled
+             */
+            phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL;
+            if(hw->disable_polarity_correction == 1)
+                phy_data |= M88E1000_PSCR_POLARITY_REVERSAL;
+            if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL,
+                                              phy_data)))
+                return ret_val;
 
-        /* Options:
-         *   disable_polarity_correction = 0 (default)
-         *       Automatic Correction for Reversed Cable Polarity
-         *   0 - Disabled
-         *   1 - Enabled
-         */
-        phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL;
-        if(hw->disable_polarity_correction == 1)
-            phy_data |= M88E1000_PSCR_POLARITY_REVERSAL;
-        if(e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data) < 0) {
-            DEBUGOUT("PHY Write Error\n");
-            return -E1000_ERR_PHY;
-        }
+            /* Force TX_CLK in the Extended PHY Specific Control Register
+             * to 25MHz clock.
+             */
+            if((ret_val = e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
+                                             &phy_data)))
+                return ret_val;
 
-        /* Force TX_CLK in the Extended PHY Specific Control Register
-         * to 25MHz clock.
-         */
-        if(e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
-        phy_data |= M88E1000_EPSCR_TX_CLK_25;
+            phy_data |= M88E1000_EPSCR_TX_CLK_25;
 
-        if (hw->phy_revision < M88E1011_I_REV_4) {
-            /* Configure Master and Slave downshift values */
-            phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK |
-                          M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK);
-            phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X |
-                         M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X);
-            if(e1000_write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
-                                   phy_data) < 0) {
-                DEBUGOUT("PHY Write Error\n");
-                return -E1000_ERR_PHY;
+            if (hw->phy_revision < M88E1011_I_REV_4) {
+                /* Configure Master and Slave downshift values */
+                phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK |
+                              M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK);
+                phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X |
+                             M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X);
+                if((ret_val = e1000_write_phy_reg(hw,
+                                                  M88E1000_EXT_PHY_SPEC_CTRL,
+                                                  phy_data)))
+                    return ret_val;
             }
-        }
 
-        /* SW Reset the PHY so all changes take effect */
-        ret_val = e1000_phy_reset(hw);
-        if(ret_val < 0) {
-            DEBUGOUT("Error Resetting the PHY\n");
-            return ret_val;
+            /* SW Reset the PHY so all changes take effect */
+            if((ret_val = e1000_phy_reset(hw))) {
+                DEBUGOUT("Error Resetting the PHY\n");
+                return ret_val;
+            }
         }
-    }
 
-    /* Options:
-     *   autoneg = 1 (default)
-     *      PHY will advertise value(s) parsed from
-     *      autoneg_advertised and fc
-     *   autoneg = 0
-     *      PHY will be set to 10H, 10F, 100H, or 100F
-     *      depending on value parsed from forced_speed_duplex.
-     */
+        /* Options:
+         *   autoneg = 1 (default)
+         *      PHY will advertise value(s) parsed from
+         *      autoneg_advertised and fc
+         *   autoneg = 0
+         *      PHY will be set to 10H, 10F, 100H, or 100F
+         *      depending on value parsed from forced_speed_duplex.
+         */
+
+        /* Is autoneg enabled?  This is enabled by default or by software
+         * override.  If so, call e1000_phy_setup_autoneg routine to parse the
+         * autoneg_advertised and fc options. If autoneg is NOT enabled, then
+         * the user should have provided a speed/duplex override.  If so, then
+         * call e1000_phy_force_speed_duplex to parse and set this up.
+         */
+        if(hw->autoneg) {
+            /* Perform some bounds checking on the hw->autoneg_advertised
+             * parameter.  If this variable is zero, then set it to the default.
+             */
+            hw->autoneg_advertised &= AUTONEG_ADVERTISE_SPEED_DEFAULT;
 
-    /* Is autoneg enabled?  This is enabled by default or by software override.
-     * If so, call e1000_phy_setup_autoneg routine to parse the
-     * autoneg_advertised and fc options. If autoneg is NOT enabled, then the
-     * user should have provided a speed/duplex override.  If so, then call
-     * e1000_phy_force_speed_duplex to parse and set this up.
-     */
-    if(hw->autoneg) {
-        /* Perform some bounds checking on the hw->autoneg_advertised
-         * parameter.  If this variable is zero, then set it to the default.
-         */
-        hw->autoneg_advertised &= AUTONEG_ADVERTISE_SPEED_DEFAULT;
+            /* If autoneg_advertised is zero, we assume it was not defaulted
+             * by the calling code so we set to advertise full capability.
+             */
+            if(hw->autoneg_advertised == 0)
+                hw->autoneg_advertised = AUTONEG_ADVERTISE_SPEED_DEFAULT;
 
-        /* If autoneg_advertised is zero, we assume it was not defaulted
-         * by the calling code so we set to advertise full capability.
-         */
-        if(hw->autoneg_advertised == 0)
-            hw->autoneg_advertised = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+            DEBUGOUT("Reconfiguring auto-neg advertisement params\n");
+            if((ret_val = e1000_phy_setup_autoneg(hw))) {
+                DEBUGOUT("Error Setting up Auto-Negotiation\n");
+                return ret_val;
+            }
+            DEBUGOUT("Restarting Auto-Neg\n");
 
-        DEBUGOUT("Reconfiguring auto-neg advertisement params\n");
-        ret_val = e1000_phy_setup_autoneg(hw);
-        if(ret_val < 0) {
-            DEBUGOUT("Error Setting up Auto-Negotiation\n");
-            return ret_val;
-        }
-        DEBUGOUT("Restarting Auto-Neg\n");
+            /* Restart auto-negotiation by setting the Auto Neg Enable bit and
+             * the Auto Neg Restart bit in the PHY control register.
+             */
+            if((ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data)))
+                return ret_val;
 
-        /* Restart auto-negotiation by setting the Auto Neg Enable bit and
-         * the Auto Neg Restart bit in the PHY control register.
-         */
-        if(e1000_read_phy_reg(hw, PHY_CTRL, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
-        phy_data |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG);
-        if(e1000_write_phy_reg(hw, PHY_CTRL, phy_data) < 0) {
-            DEBUGOUT("PHY Write Error\n");
-            return -E1000_ERR_PHY;
-        }
+            phy_data |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG);
+            if((ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data)))
+                return ret_val;
 
-        /* Does the user want to wait for Auto-Neg to complete here, or
-         * check at a later time (for example, callback routine).
-         */
-        if(hw->wait_autoneg_complete) {
-            ret_val = e1000_wait_autoneg(hw);
-            if(ret_val < 0) {
-                DEBUGOUT("Error while waiting for autoneg to complete\n");
+            /* Does the user want to wait for Auto-Neg to complete here, or
+             * check at a later time (for example, callback routine).
+             */
+            if(hw->wait_autoneg_complete) {
+                if((ret_val = e1000_wait_autoneg(hw))) {
+                    DEBUGOUT("Error while waiting for autoneg to complete\n");
+                    return ret_val;
+                }
+            }
+            hw->get_link_status = TRUE;
+        } else {
+            DEBUGOUT("Forcing speed and duplex\n");
+            if((ret_val = e1000_phy_force_speed_duplex(hw))) {
+                DEBUGOUT("Error Forcing Speed and Duplex\n");
                 return ret_val;
             }
         }
-        hw->get_link_status = TRUE;
-    } else {
-        DEBUGOUT("Forcing speed and duplex\n");
-        ret_val = e1000_phy_force_speed_duplex(hw);
-        if(ret_val < 0) {
-            DEBUGOUT("Error Forcing Speed and Duplex\n");
-            return ret_val;
-        }
-    }
+    } /* !hw->phy_reset_disable */
 
     /* Check link status. Wait up to 100 microseconds for link to become
      * valid.
      */
     for(i = 0; i < 10; i++) {
-        if(e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
-        if(e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+            return ret_val;
+        if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+            return ret_val;
+
         if(phy_data & MII_SR_LINK_STATUS) {
             /* We have link, so we need to finish the config process:
              *   1) Set up the MAC to the current PHY speed/duplex
@@ -995,25 +1177,31 @@
             if(hw->mac_type >= e1000_82544) {
                 e1000_config_collision_dist(hw);
             } else {
-                ret_val = e1000_config_mac_to_phy(hw);
-                if(ret_val < 0) {
+                if((ret_val = e1000_config_mac_to_phy(hw))) {
                     DEBUGOUT("Error configuring MAC to PHY settings\n");
                     return ret_val;
-                  }
+                }
             }
-            ret_val = e1000_config_fc_after_link_up(hw);
-            if(ret_val < 0) {
+            if((ret_val = e1000_config_fc_after_link_up(hw))) {
                 DEBUGOUT("Error Configuring Flow Control\n");
                 return ret_val;
             }
             DEBUGOUT("Valid link established!!!\n");
-            return 0;
+
+            if(hw->phy_type == e1000_phy_igp) {
+                if((ret_val = e1000_config_dsp_after_link_change(hw, TRUE))) {
+                    DEBUGOUT("Error Configuring DSP after link up\n");
+                    return ret_val;
+                }
+            }
+            DEBUGOUT("Valid link established!!!\n");
+            return E1000_SUCCESS;
         }
         udelay(10);
     }
 
     DEBUGOUT("Unable to establish link!!!\n");
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -1024,22 +1212,20 @@
 int32_t
 e1000_phy_setup_autoneg(struct e1000_hw *hw)
 {
+    int32_t ret_val;
     uint16_t mii_autoneg_adv_reg;
     uint16_t mii_1000t_ctrl_reg;
 
     DEBUGFUNC("e1000_phy_setup_autoneg");
 
     /* Read the MII Auto-Neg Advertisement Register (Address 4). */
-    if(e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
+    if((ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV,
+                                     &mii_autoneg_adv_reg)))
+        return ret_val;
 
     /* Read the MII 1000Base-T Control Register (Address 9). */
-    if(e1000_read_phy_reg(hw, PHY_1000T_CTRL, &mii_1000t_ctrl_reg) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
+    if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &mii_1000t_ctrl_reg)))
+        return ret_val;
 
     /* Need to parse both autoneg_advertised and fc and set up
      * the appropriate PHY registers.  First we will parse for
@@ -1145,18 +1331,16 @@
         return -E1000_ERR_CONFIG;
     }
 
-    if(e1000_write_phy_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg) < 0) {
-        DEBUGOUT("PHY Write Error\n");
-        return -E1000_ERR_PHY;
-    }
+    if((ret_val = e1000_write_phy_reg(hw, PHY_AUTONEG_ADV,
+                                      mii_autoneg_adv_reg)))
+        return ret_val;
 
     DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg);
 
-    if(e1000_write_phy_reg(hw, PHY_1000T_CTRL, mii_1000t_ctrl_reg) < 0) {
-        DEBUGOUT("PHY Write Error\n");
-        return -E1000_ERR_PHY;
-    }
-    return 0;
+    if((ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, mii_1000t_ctrl_reg)))
+        return ret_val;
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -1192,10 +1376,8 @@
     ctrl &= ~E1000_CTRL_ASDE;
 
     /* Read the MII Control Register. */
-    if(e1000_read_phy_reg(hw, PHY_CTRL, &mii_ctrl_reg) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
+    if((ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &mii_ctrl_reg)))
+        return ret_val;
 
     /* We need to disable autoneg in order to force link and duplex. */
 
@@ -1241,19 +1423,18 @@
     E1000_WRITE_REG(hw, CTRL, ctrl);
 
     if (hw->phy_type == e1000_phy_m88) {
-        if(e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL,
+                                         &phy_data)))
+            return ret_val;
 
         /* Clear Auto-Crossover to force MDI manually. M88E1000 requires MDI
          * forced whenever speed are duplex are forced.
          */
         phy_data &= ~M88E1000_PSCR_AUTO_X_MODE;
-        if(e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data) < 0) {
-            DEBUGOUT("PHY Write Error\n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL,
+                                          phy_data)))
+            return ret_val;
+
         DEBUGOUT1("M88E1000 PSCR: %x \n", phy_data);
 
         /* Need to reset the PHY or these changes will be ignored */
@@ -1262,26 +1443,23 @@
         /* Clear Auto-Crossover to force MDI manually.  IGP requires MDI
          * forced whenever speed or duplex are forced.
          */
-        if(e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL,
+                                         &phy_data)))
+            return ret_val;
 
         phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX;
         phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX;
 
-        if(e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data) < 0) {
-            DEBUGOUT("PHY Write Error\n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL,
+                                          phy_data)))
+            return ret_val;
     }
 
     /* Write back the modified PHY MII control register. */
-    if(e1000_write_phy_reg(hw, PHY_CTRL, mii_ctrl_reg) < 0) {
-        DEBUGOUT("PHY Write Error\n");
-        return -E1000_ERR_PHY;
-    }
     udelay(1);
+    if((ret_val = e1000_write_phy_reg(hw, PHY_CTRL, mii_ctrl_reg)))
+        return ret_val;
+
 
     /* The wait_autoneg_complete flag may be a little misleading here.
      * Since we are forcing speed and duplex, Auto-Neg is not enabled.
@@ -1300,22 +1478,18 @@
             /* Read the MII Status Register and wait for Auto-Neg Complete bit
              * to be set.
              */
-            if(e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg) < 0) {
-                DEBUGOUT("PHY Read Error\n");
-                return -E1000_ERR_PHY;
-            }
-            if(e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg) < 0) {
-                DEBUGOUT("PHY Read Error\n");
-                return -E1000_ERR_PHY;
-            }
+            if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg)))
+                return ret_val;
+
+            if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg)))
+                return ret_val;
+
             if(mii_status_reg & MII_SR_LINK_STATUS) break;
             msec_delay(100);
         }
         if(i == 0) { /* We didn't get link */
             /* Reset the DSP and wait again for link. */
-
-            ret_val = e1000_phy_reset_dsp(hw);
-            if(ret_val < 0) {
+            if((ret_val = e1000_phy_reset_dsp(hw))) {
                 DEBUGOUT("Error Resetting PHY DSP\n");
                 return ret_val;
             }
@@ -1327,14 +1501,11 @@
             /* Read the MII Status Register and wait for Auto-Neg Complete bit
              * to be set.
              */
-            if(e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg) < 0) {
-                DEBUGOUT("PHY Read Error\n");
-                return -E1000_ERR_PHY;
-            }
-            if(e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg) < 0) {
-                DEBUGOUT("PHY Read Error\n");
-                return -E1000_ERR_PHY;
-            }
+            if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg)))
+                return ret_val;
+
+            if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg)))
+                return ret_val;
         }
     }
 
@@ -1343,30 +1514,29 @@
          * Extended PHY Specific Control Register to 25MHz clock.  This value
          * defaults back to a 2.5MHz clock when the PHY is reset.
          */
-        if(e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
+                                         &phy_data)))
+            return ret_val;
+
         phy_data |= M88E1000_EPSCR_TX_CLK_25;
-        if(e1000_write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data) < 0) {
-            DEBUGOUT("PHY Write Error\n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
+                                          phy_data)))
+            return ret_val;
 
         /* In addition, because of the s/w reset above, we need to enable CRS on
          * TX.  This must be set for both full and half duplex operation.
          */
-        if(e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL,
+                                         &phy_data)))
+            return ret_val;
+
         phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
-        if(e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data) < 0) {
-            DEBUGOUT("PHY Write Error\n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL,
+                                          phy_data)))
+            return ret_val;
+
     }
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -1406,6 +1576,7 @@
 e1000_config_mac_to_phy(struct e1000_hw *hw)
 {
     uint32_t ctrl;
+    int32_t ret_val;
     uint16_t phy_data;
 
     DEBUGFUNC("e1000_config_mac_to_phy");
@@ -1421,10 +1592,10 @@
      * registers depending on negotiated values.
      */
     if (hw->phy_type == e1000_phy_igp) {
-        if(e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS,
+                                         &phy_data)))
+            return ret_val;
+
         if(phy_data & IGP01E1000_PSSR_FULL_DUPLEX) ctrl |= E1000_CTRL_FD;
         else ctrl &= ~E1000_CTRL_FD;
 
@@ -1440,10 +1611,10 @@
                 IGP01E1000_PSSR_SPEED_100MBPS)
             ctrl |= E1000_CTRL_SPD_100;
     } else {
-        if(e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS,
+                                         &phy_data)))
+            return ret_val;
+
         if(phy_data & M88E1000_PSSR_DPLX) ctrl |= E1000_CTRL_FD;
         else ctrl &= ~E1000_CTRL_FD;
 
@@ -1459,7 +1630,7 @@
     }
     /* Write the configured values back to the Device Control Reg. */
     E1000_WRITE_REG(hw, CTRL, ctrl);
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -1473,7 +1644,7 @@
  * by the PHY rather than the MAC. Software must also configure these
  * bits when link is forced on a fiber connection.
  *****************************************************************************/
-static int32_t
+int32_t
 e1000_force_mac_fc(struct e1000_hw *hw)
 {
     uint32_t ctrl;
@@ -1526,7 +1697,7 @@
         ctrl &= (~E1000_CTRL_TFCE);
 
     E1000_WRITE_REG(hw, CTRL, ctrl);
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -1557,9 +1728,9 @@
      * configuration of the MAC to match the "fc" parameter.
      */
     if(((hw->media_type == e1000_media_type_fiber) && (hw->autoneg_failed)) ||
+       ((hw->media_type == e1000_media_type_internal_serdes) && (hw->autoneg_failed)) ||
        ((hw->media_type == e1000_media_type_copper) && (!hw->autoneg))) {
-        ret_val = e1000_force_mac_fc(hw);
-        if(ret_val < 0) {
+        if((ret_val = e1000_force_mac_fc(hw))) {
             DEBUGOUT("Error forcing flow control settings\n");
             return ret_val;
         }
@@ -1575,14 +1746,10 @@
          * has completed.  We read this twice because this reg has
          * some "sticky" (latched) bits.
          */
-        if(e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg) < 0) {
-            DEBUGOUT("PHY Read Error \n");
-            return -E1000_ERR_PHY;
-        }
-        if(e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg) < 0) {
-            DEBUGOUT("PHY Read Error \n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg)))
+            return ret_val;
+        if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg)))
+            return ret_val;
 
         if(mii_status_reg & MII_SR_AUTONEG_COMPLETE) {
             /* The AutoNeg process has completed, so we now need to
@@ -1591,14 +1758,12 @@
              * Register (Address 5) to determine how flow control was
              * negotiated.
              */
-            if(e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, &mii_nway_adv_reg) < 0) {
-                DEBUGOUT("PHY Read Error\n");
-                return -E1000_ERR_PHY;
-            }
-            if(e1000_read_phy_reg(hw, PHY_LP_ABILITY, &mii_nway_lp_ability_reg) < 0) {
-                DEBUGOUT("PHY Read Error\n");
-                return -E1000_ERR_PHY;
-            }
+            if((ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV,
+                                             &mii_nway_adv_reg)))
+                return ret_val;
+            if((ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY,
+                                             &mii_nway_lp_ability_reg)))
+                return ret_val;
 
             /* Two bits in the Auto Negotiation Advertisement Register
              * (Address 4) and two bits in the Auto Negotiation Base
@@ -1704,7 +1869,7 @@
                     hw->original_fc == e1000_fc_tx_pause) {
                 hw->fc = e1000_fc_none;
                 DEBUGOUT("Flow Control = NONE.\r\n");
-            } else {
+            } else if(!hw->fc_strict_ieee) {
                 hw->fc = e1000_fc_rx_pause;
                 DEBUGOUT("Flow Control = RX PAUSE frames only.\r\n");
             }
@@ -1713,7 +1878,10 @@
              * negotiated to HALF DUPLEX, flow control should not be
              * enabled per IEEE 802.3 spec.
              */
-            e1000_get_speed_and_duplex(hw, &speed, &duplex);
+            if((ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex))) {
+                DEBUGOUT("Error getting link speed and duplex\n");
+                return ret_val;
+            }
 
             if(duplex == HALF_DUPLEX)
                 hw->fc = e1000_fc_none;
@@ -1721,16 +1889,15 @@
             /* Now we call a subroutine to actually force the MAC
              * controller to use the correct flow control settings.
              */
-            ret_val = e1000_force_mac_fc(hw);
-            if(ret_val < 0) {
+            if((ret_val = e1000_force_mac_fc(hw))) {
                 DEBUGOUT("Error forcing flow control settings\n");
                 return ret_val;
-             }
+            }
         } else {
             DEBUGOUT("Copper PHY and Auto Neg has not completed.\r\n");
         }
     }
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -1747,19 +1914,19 @@
     uint32_t ctrl;
     uint32_t status;
     uint32_t rctl;
-    uint32_t signal;
+    uint32_t signal = 0;
     int32_t ret_val;
     uint16_t phy_data;
     uint16_t lp_capability;
 
     DEBUGFUNC("e1000_check_for_link");
 
-    /* On adapters with a MAC newer that 82544, SW Defineable pin 1 will be
+    /* On adapters with a MAC newer than 82544, SW Defineable pin 1 will be
      * set when the optics detect a signal. On older adapters, it will be
-     * cleared when there is a signal
+     * cleared when there is a signal.  This applies to fiber media only.
      */
-    if(hw->mac_type > e1000_82544) signal = E1000_CTRL_SWDPIN1;
-    else signal = 0;
+    if(hw->media_type == e1000_media_type_fiber)
+        signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0;
 
     ctrl = E1000_READ_REG(hw, CTRL);
     status = E1000_READ_REG(hw, STATUS);
@@ -1777,14 +1944,10 @@
          * of the PHY.
          * Read the register twice since the link bit is sticky.
          */
-        if(e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
-        if(e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+            return ret_val;
+        if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+            return ret_val;
 
         if(phy_data & MII_SR_LINK_STATUS) {
             hw->get_link_status = FALSE;
@@ -1794,6 +1957,7 @@
 
         } else {
             /* No link detected */
+            e1000_config_dsp_after_link_change(hw, FALSE);
             return 0;
         }
 
@@ -1802,6 +1966,9 @@
          */
         if(!hw->autoneg) return -E1000_ERR_CONFIG;
 
+        /* optimize the dsp settings for the igp phy */
+        e1000_config_dsp_after_link_change(hw, TRUE);
+
         /* We have a M88E1000 PHY and Auto-Neg is enabled.  If we
          * have Si on board that is 82544 or newer, Auto
          * Speed Detection takes care of MAC speed/duplex
@@ -1813,8 +1980,7 @@
         if(hw->mac_type >= e1000_82544)
             e1000_config_collision_dist(hw);
         else {
-            ret_val = e1000_config_mac_to_phy(hw);
-            if(ret_val < 0) {
+            if((ret_val = e1000_config_mac_to_phy(hw))) {
                 DEBUGOUT("Error configuring MAC to PHY settings\n");
                 return ret_val;
             }
@@ -1824,8 +1990,7 @@
          * need to restore the desired flow control settings because we may
          * have had to re-autoneg with a different link partner.
          */
-        ret_val = e1000_config_fc_after_link_up(hw);
-        if(ret_val < 0) {
+        if((ret_val = e1000_config_fc_after_link_up(hw))) {
             DEBUGOUT("Error configuring flow control\n");
             return ret_val;
         }
@@ -1840,10 +2005,9 @@
          * partner is TBI-based, and we turn on TBI Compatibility.
          */
         if(hw->tbi_compatibility_en) {
-            if(e1000_read_phy_reg(hw, PHY_LP_ABILITY, &lp_capability) < 0) {
-                DEBUGOUT("PHY Read Error\n");
-                return -E1000_ERR_PHY;
-            }
+            if((ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY,
+                                             &lp_capability)))
+                return ret_val;
             if(lp_capability & (NWAY_LPAR_10T_HD_CAPS |
                                 NWAY_LPAR_10T_FD_CAPS |
                                 NWAY_LPAR_100TX_HD_CAPS |
@@ -1900,8 +2064,7 @@
         E1000_WRITE_REG(hw, CTRL, ctrl);
 
         /* Configure Flow Control after forcing link up. */
-        ret_val = e1000_config_fc_after_link_up(hw);
-        if(ret_val < 0) {
+        if((ret_val = e1000_config_fc_after_link_up(hw))) {
             DEBUGOUT("Error configuring flow control\n");
             return ret_val;
         }
@@ -1918,7 +2081,7 @@
         E1000_WRITE_REG(hw, TXCW, hw->txcw);
         E1000_WRITE_REG(hw, CTRL, (ctrl & ~E1000_CTRL_SLU));
     }
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -1928,12 +2091,14 @@
  * speed - Speed of the connection
  * duplex - Duplex setting of the connection
  *****************************************************************************/
-void
+int32_t
 e1000_get_speed_and_duplex(struct e1000_hw *hw,
                            uint16_t *speed,
                            uint16_t *duplex)
 {
     uint32_t status;
+    int32_t ret_val;
+    uint16_t phy_data;
 
     DEBUGFUNC("e1000_get_speed_and_duplex");
 
@@ -1962,6 +2127,27 @@
         *speed = SPEED_1000;
         *duplex = FULL_DUPLEX;
     }
+
+    /* IGP01 PHY may advertise full duplex operation after speed downgrade even
+     * if it is operating at half duplex.  Here we set the duplex settings to
+     * match the duplex in the link partner's capabilities.
+     */
+    if(hw->phy_type == e1000_phy_igp && hw->speed_downgraded) {
+        if((ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_EXP, &phy_data)))
+            return ret_val;
+
+        if(!(phy_data & NWAY_ER_LP_NWAY_CAPS))
+            *duplex = HALF_DUPLEX;
+        else {
+            if((ret_val == e1000_read_phy_reg(hw, PHY_LP_ABILITY, &phy_data)))
+                return ret_val;
+            if((*speed == SPEED_100 && !(phy_data & NWAY_LPAR_100TX_FD_CAPS)) ||
+               (*speed == SPEED_10 && !(phy_data & NWAY_LPAR_10T_FD_CAPS)))
+                *duplex = HALF_DUPLEX;
+        }
+    }
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -1972,6 +2158,7 @@
 int32_t
 e1000_wait_autoneg(struct e1000_hw *hw)
 {
+    int32_t ret_val;
     uint16_t i;
     uint16_t phy_data;
 
@@ -1983,20 +2170,16 @@
         /* Read the MII Status Register and wait for Auto-Neg
          * Complete bit to be set.
          */
-        if(e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
-        if(e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+            return ret_val;
+        if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+            return ret_val;
         if(phy_data & MII_SR_AUTONEG_COMPLETE) {
-            return 0;
+            return E1000_SUCCESS;
         }
         msec_delay(100);
     }
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -2010,11 +2193,11 @@
                     uint32_t *ctrl)
 {
     /* Raise the clock input to the Management Data Clock (by setting the MDC
-     * bit), and then delay 2 microseconds.
+     * bit), and then delay 10 microseconds.
      */
     E1000_WRITE_REG(hw, CTRL, (*ctrl | E1000_CTRL_MDC));
     E1000_WRITE_FLUSH(hw);
-    udelay(2);
+    udelay(10);
 }
 
 /******************************************************************************
@@ -2028,11 +2211,11 @@
                     uint32_t *ctrl)
 {
     /* Lower the clock input to the Management Data Clock (by clearing the MDC
-     * bit), and then delay 2 microseconds.
+     * bit), and then delay 10 microseconds.
      */
     E1000_WRITE_REG(hw, CTRL, (*ctrl & ~E1000_CTRL_MDC));
     E1000_WRITE_FLUSH(hw);
-    udelay(2);
+    udelay(10);
 }
 
 /******************************************************************************
@@ -2076,7 +2259,7 @@
         E1000_WRITE_REG(hw, CTRL, ctrl);
         E1000_WRITE_FLUSH(hw);
 
-        udelay(2);
+        udelay(10);
 
         e1000_raise_mdi_clk(hw, &ctrl);
         e1000_lower_mdi_clk(hw, &ctrl);
@@ -2138,8 +2321,8 @@
 }
 
 /*****************************************************************************
-* Reads the value from a PHY register
-*
+* Reads the value from a PHY register, if the value is on a specific non zero
+* page, sets the page first.
 * hw - Struct containing variables accessed by shared code
 * reg_addr - address of the PHY register to read
 ******************************************************************************/
@@ -2148,11 +2331,33 @@
                    uint32_t reg_addr,
                    uint16_t *phy_data)
 {
+    uint32_t ret_val;
+
+    DEBUGFUNC("e1000_read_phy_reg");
+
+    if(hw->phy_type == e1000_phy_igp &&
+       (reg_addr > MAX_PHY_MULTI_PAGE_REG)) {
+        if((ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT,
+                                             (uint16_t)reg_addr)))
+            return ret_val;
+    }
+
+    ret_val = e1000_read_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT & reg_addr,
+                                    phy_data);
+
+    return ret_val;
+}
+
+int32_t
+e1000_read_phy_reg_ex(struct e1000_hw *hw,
+                      uint32_t reg_addr,
+                      uint16_t *phy_data)
+{
     uint32_t i;
     uint32_t mdic = 0;
     const uint32_t phy_addr = 1;
 
-    DEBUGFUNC("e1000_read_phy_reg");
+    DEBUGFUNC("e1000_read_phy_reg_ex");
 
     if(reg_addr > MAX_PHY_REG_ADDRESS) {
         DEBUGOUT1("PHY Address %d is out of range\n", reg_addr);
@@ -2172,7 +2377,7 @@
 
         /* Poll the ready bit to see if the MDI read completed */
         for(i = 0; i < 64; i++) {
-            udelay(10);
+            udelay(50);
             mdic = E1000_READ_REG(hw, MDIC);
             if(mdic & E1000_MDIC_READY) break;
         }
@@ -2214,7 +2419,7 @@
          */
         *phy_data = e1000_shift_in_mdi_bits(hw);
     }
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -2229,11 +2434,33 @@
                     uint32_t reg_addr,
                     uint16_t phy_data)
 {
+    uint32_t ret_val;
+
+    DEBUGFUNC("e1000_write_phy_reg");
+
+    if(hw->phy_type == e1000_phy_igp &&
+       (reg_addr > MAX_PHY_MULTI_PAGE_REG)) {
+        if((ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT,
+                                             (uint16_t)reg_addr)))
+            return ret_val;
+    }
+
+    ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT & reg_addr,
+                                     phy_data);
+
+    return ret_val;
+}
+
+int32_t
+e1000_write_phy_reg_ex(struct e1000_hw *hw,
+                    uint32_t reg_addr,
+                    uint16_t phy_data)
+{
     uint32_t i;
     uint32_t mdic = 0;
     const uint32_t phy_addr = 1;
 
-    DEBUGFUNC("e1000_write_phy_reg");
+    DEBUGFUNC("e1000_write_phy_reg_ex");
 
     if(reg_addr > MAX_PHY_REG_ADDRESS) {
         DEBUGOUT1("PHY Address %d is out of range\n", reg_addr);
@@ -2254,7 +2481,7 @@
 
         /* Poll the ready bit to see if the MDI read completed */
         for(i = 0; i < 64; i++) {
-            udelay(10);
+            udelay(50);
             mdic = E1000_READ_REG(hw, MDIC);
             if(mdic & E1000_MDIC_READY) break;
         }
@@ -2284,7 +2511,7 @@
         e1000_shift_out_mdi_bits(hw, mdic, 32);
     }
 
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -2329,11 +2556,6 @@
     udelay(150);
 
     if((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) {
-        if(e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT, 0x0000) < 0) {
-            DEBUGOUT("PHY Write Error\n");
-            return;
-        }
-
         /* Configure activity LED after PHY reset */
         led_ctrl = E1000_READ_REG(hw, LEDCTL);
         led_ctrl &= IGP_ACTIVITY_LED_MASK;
@@ -2352,24 +2574,26 @@
 int32_t
 e1000_phy_reset(struct e1000_hw *hw)
 {
+    int32_t ret_val;
     uint16_t phy_data;
 
     DEBUGFUNC("e1000_phy_reset");
 
-    if(e1000_read_phy_reg(hw, PHY_CTRL, &phy_data) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
-    phy_data |= MII_CR_RESET;
-    if(e1000_write_phy_reg(hw, PHY_CTRL, phy_data) < 0) {
-        DEBUGOUT("PHY Write Error\n");
-        return -E1000_ERR_PHY;
-    }
-    udelay(1);
-    if (hw->phy_type == e1000_phy_igp) {
+    if(hw->mac_type != e1000_82541_rev_2) {
+        if((ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data)))
+            return ret_val;
+
+        phy_data |= MII_CR_RESET;
+        if((ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data)))
+            return ret_val;
+
+        udelay(1);
+    } else e1000_phy_hw_reset(hw);
+
+    if(hw->phy_type == e1000_phy_igp)
         e1000_phy_init_script(hw);
-    }
-    return 0;
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -2380,23 +2604,21 @@
 int32_t
 e1000_detect_gig_phy(struct e1000_hw *hw)
 {
+    int32_t phy_init_status, ret_val;
     uint16_t phy_id_high, phy_id_low;
     boolean_t match = FALSE;
-    int32_t phy_init_status;
 
     DEBUGFUNC("e1000_detect_gig_phy");
 
     /* Read the PHY ID Registers to identify which PHY is onboard. */
-    if(e1000_read_phy_reg(hw, PHY_ID1, &phy_id_high) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
+    if((ret_val = e1000_read_phy_reg(hw, PHY_ID1, &phy_id_high)))
+        return ret_val;
+
     hw->phy_id = (uint32_t) (phy_id_high << 16);
     udelay(20);
-    if(e1000_read_phy_reg(hw, PHY_ID2, &phy_id_low) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
+    if((ret_val = e1000_read_phy_reg(hw, PHY_ID2, &phy_id_low)))
+        return ret_val;
+
     hw->phy_id |= (uint32_t) (phy_id_low & PHY_REVISION_MASK);
     hw->phy_revision = (uint32_t) phy_id_low & ~PHY_REVISION_MASK;
 
@@ -2409,11 +2631,15 @@
         break;
     case e1000_82540:
     case e1000_82545:
+    case e1000_82545_rev_3:
     case e1000_82546:
+    case e1000_82546_rev_3:
         if(hw->phy_id == M88E1011_I_PHY_ID) match = TRUE;
         break;
     case e1000_82541:
+    case e1000_82541_rev_2:
     case e1000_82547:
+    case e1000_82547_rev_2:
         if(hw->phy_id == IGP01E1000_I_PHY_ID) match = TRUE;
         break;
     default:
@@ -2424,7 +2650,7 @@
 
     if ((match) && (phy_init_status == E1000_SUCCESS)) {
         DEBUGOUT1("PHY ID 0x%X detected\n", hw->phy_id);
-        return 0;
+        return E1000_SUCCESS;
     }
     DEBUGOUT1("Invalid PHY ID 0x%X\n", hw->phy_id);
     return -E1000_ERR_PHY;
@@ -2438,17 +2664,16 @@
 static int32_t
 e1000_phy_reset_dsp(struct e1000_hw *hw)
 {
-    int32_t ret_val = -E1000_ERR_PHY;
+    int32_t ret_val;
     DEBUGFUNC("e1000_phy_reset_dsp");
 
     do {
-        if(e1000_write_phy_reg(hw, 29, 0x001d) < 0) break;
-        if(e1000_write_phy_reg(hw, 30, 0x00c1) < 0) break;
-        if(e1000_write_phy_reg(hw, 30, 0x0000) < 0) break;
-        ret_val = 0;
+        if((ret_val = e1000_write_phy_reg(hw, 29, 0x001d))) break;
+        if((ret_val = e1000_write_phy_reg(hw, 30, 0x00c1))) break;
+        if((ret_val = e1000_write_phy_reg(hw, 30, 0x0000))) break;
+        ret_val = E1000_SUCCESS;
     } while(0);
 
-    if(ret_val < 0) DEBUGOUT("PHY Write Error\n");
     return ret_val;
 }
 
@@ -2459,8 +2684,10 @@
 * phy_info - PHY information structure
 ******************************************************************************/
 int32_t
-e1000_phy_igp_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info)
+e1000_phy_igp_get_info(struct e1000_hw *hw,
+                       struct e1000_phy_info *phy_info)
 {
+    int32_t ret_val;
     uint16_t phy_data, polarity, min_length, max_length, average;
 
     DEBUGFUNC("e1000_phy_igp_get_info");
@@ -2476,13 +2703,14 @@
     phy_info->polarity_correction = e1000_polarity_reversal_enabled;
 
     /* Check polarity status */
-    if(e1000_check_polarity(hw, &polarity) < 0)
-        return -E1000_ERR_PHY;
+    if((ret_val = e1000_check_polarity(hw, &polarity)))
+        return ret_val;
 
     phy_info->cable_polarity = polarity;
 
-    if(e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, &phy_data) < 0)
-        return -E1000_ERR_PHY;
+    if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS,
+                                     &phy_data)))
+        return ret_val;
 
     phy_info->mdix_mode = (phy_data & IGP01E1000_PSSR_MDIX) >>
                           IGP01E1000_PSSR_MDIX_SHIFT;
@@ -2490,8 +2718,8 @@
     if((phy_data & IGP01E1000_PSSR_SPEED_MASK) ==
        IGP01E1000_PSSR_SPEED_1000MBPS) {
         /* Local/Remote Receiver Information are only valid at 1000 Mbps */
-        if(e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data) < 0)
-            return -E1000_ERR_PHY;
+        if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data)))
+            return ret_val;
 
         phy_info->local_rx = (phy_data & SR_1000T_LOCAL_RX_STATUS) >>
                              SR_1000T_LOCAL_RX_STATUS_SHIFT;
@@ -2499,8 +2727,8 @@
                               SR_1000T_REMOTE_RX_STATUS_SHIFT;
 
         /* Get cable length */
-        if(e1000_get_cable_length(hw, &min_length, &max_length) < 0)
-            return -E1000_ERR_PHY;
+        if((ret_val = e1000_get_cable_length(hw, &min_length, &max_length)))
+            return ret_val;
 
         /* transalte to old method */
         average = (max_length + min_length) / 2;
@@ -2527,8 +2755,10 @@
 * phy_info - PHY information structure
 ******************************************************************************/
 int32_t
-e1000_phy_m88_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info)
+e1000_phy_m88_get_info(struct e1000_hw *hw,
+                       struct e1000_phy_info *phy_info)
 {
+    int32_t ret_val;
     uint16_t phy_data, polarity;
 
     DEBUGFUNC("e1000_phy_m88_get_info");
@@ -2537,8 +2767,8 @@
      * and it stored in the hw->speed_downgraded parameter. */
     phy_info->downshift = hw->speed_downgraded;
 
-    if(e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data) < 0)
-        return -E1000_ERR_PHY;
+    if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data)))
+        return ret_val;
 
     phy_info->extended_10bt_distance =
         (phy_data & M88E1000_PSCR_10BT_EXT_DIST_ENABLE) >>
@@ -2548,13 +2778,13 @@
         M88E1000_PSCR_POLARITY_REVERSAL_SHIFT;
 
     /* Check polarity status */
-    if(e1000_check_polarity(hw, &polarity) < 0)
-        return -E1000_ERR_PHY;
+    if((ret_val = e1000_check_polarity(hw, &polarity)))
+        return ret_val;
 
     phy_info->cable_polarity = polarity;
 
-    if(e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data) < 0)
-        return -E1000_ERR_PHY;
+    if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data)))
+        return ret_val;
 
     phy_info->mdix_mode = (phy_data & M88E1000_PSSR_MDIX) >>
                           M88E1000_PSSR_MDIX_SHIFT;
@@ -2566,8 +2796,8 @@
         phy_info->cable_length = ((phy_data & M88E1000_PSSR_CABLE_LENGTH) >>
                                   M88E1000_PSSR_CABLE_LENGTH_SHIFT);
 
-        if(e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data) < 0)
-            return -E1000_ERR_PHY;
+        if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data)))
+            return ret_val;
 
         phy_info->local_rx = (phy_data & SR_1000T_LOCAL_RX_STATUS) >>
                              SR_1000T_LOCAL_RX_STATUS_SHIFT;
@@ -2589,6 +2819,7 @@
 e1000_phy_get_info(struct e1000_hw *hw,
                    struct e1000_phy_info *phy_info)
 {
+    int32_t ret_val;
     uint16_t phy_data;
 
     DEBUGFUNC("e1000_phy_get_info");
@@ -2607,20 +2838,18 @@
         return -E1000_ERR_CONFIG;
     }
 
-    if(e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
-    if(e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) {
-        DEBUGOUT("PHY Read Error\n");
-        return -E1000_ERR_PHY;
-    }
+    if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+        return ret_val;
+
+    if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+        return ret_val;
+
     if((phy_data & MII_SR_LINK_STATUS) != MII_SR_LINK_STATUS) {
         DEBUGOUT("PHY info is only valid if link is up\n");
         return -E1000_ERR_CONFIG;
     }
 
-    if (hw->phy_type == e1000_phy_igp)
+    if(hw->phy_type == e1000_phy_igp)
         return e1000_phy_igp_get_info(hw, phy_info);
     else
         return e1000_phy_m88_get_info(hw, phy_info);
@@ -2636,7 +2865,7 @@
         hw->mdix = 1;
         return -E1000_ERR_CONFIG;
     }
-    return 0;
+    return E1000_SUCCESS;
 }
 
 
@@ -2668,7 +2897,9 @@
         break;
     case e1000_82540:
     case e1000_82545:
+    case e1000_82545_rev_3:
     case e1000_82546:
+    case e1000_82546_rev_3:
         eeprom->type = e1000_eeprom_microwire;
         eeprom->opcode_bits = 3;
         eeprom->delay_usec = 50;
@@ -2681,8 +2912,9 @@
         }
         break;
     case e1000_82541:
+    case e1000_82541_rev_2:
     case e1000_82547:
-    default:
+    case e1000_82547_rev_2:
         if (eecd & E1000_EECD_TYPE) {
             eeprom->type = e1000_eeprom_spi;
             eeprom->opcode_bits = 8;
@@ -2707,6 +2939,18 @@
             }
         }
         break;
+    default:
+        eeprom->type = e1000_eeprom_spi;
+        eeprom->opcode_bits = 8;
+        eeprom->delay_usec = 1;
+        if (eecd & E1000_EECD_ADDR_BITS) {
+            eeprom->page_size = 32;
+            eeprom->address_bits = 16;
+        } else {
+            eeprom->page_size = 8;
+            eeprom->address_bits = 8;
+        }
+        break;
     }
 
     if (eeprom->type == e1000_eeprom_spi) {
@@ -2715,28 +2959,28 @@
             eeprom_size &= EEPROM_SIZE_MASK;
 
             switch (eeprom_size) {
-                case EEPROM_SIZE_16KB:
-                    eeprom->word_size = 8192;
-                    break;
-                case EEPROM_SIZE_8KB:
-                    eeprom->word_size = 4096;
-                    break;
-                case EEPROM_SIZE_4KB:
-                    eeprom->word_size = 2048;
-                    break;
-                case EEPROM_SIZE_2KB:
-                    eeprom->word_size = 1024;
-                    break;
-                case EEPROM_SIZE_1KB:
-                    eeprom->word_size = 512;
-                    break;
-                case EEPROM_SIZE_512B:
-                    eeprom->word_size = 256;
-                    break;
-                case EEPROM_SIZE_128B:
-                default:
-                    eeprom->word_size = 64;
-                    break;
+            case EEPROM_SIZE_16KB:
+                eeprom->word_size = 8192;
+                break;
+            case EEPROM_SIZE_8KB:
+                eeprom->word_size = 4096;
+                break;
+            case EEPROM_SIZE_4KB:
+                eeprom->word_size = 2048;
+                break;
+            case EEPROM_SIZE_2KB:
+                eeprom->word_size = 1024;
+                break;
+            case EEPROM_SIZE_1KB:
+                eeprom->word_size = 512;
+                break;
+            case EEPROM_SIZE_512B:
+                eeprom->word_size = 256;
+                break;
+            case EEPROM_SIZE_128B:
+            default:
+                eeprom->word_size = 64;
+                break;
             }
         }
     }
@@ -2841,7 +3085,8 @@
  * hw - Struct containing variables accessed by shared code
  *****************************************************************************/
 static uint16_t
-e1000_shift_in_ee_bits(struct e1000_hw *hw, uint16_t count)
+e1000_shift_in_ee_bits(struct e1000_hw *hw,
+                       uint16_t count)
 {
     uint32_t eecd;
     uint32_t i;
@@ -3101,13 +3346,17 @@
     }
 
     /* Prepare the EEPROM for reading  */
-    if (e1000_acquire_eeprom(hw) != E1000_SUCCESS)
+    if(e1000_acquire_eeprom(hw) != E1000_SUCCESS)
         return -E1000_ERR_EEPROM;
 
     if(eeprom->type == e1000_eeprom_spi) {
+        uint16_t word_in;
         uint8_t read_opcode = EEPROM_READ_OPCODE_SPI;
 
-        if(e1000_spi_eeprom_ready(hw)) return -E1000_ERR_EEPROM;
+        if(e1000_spi_eeprom_ready(hw)) {
+            e1000_release_eeprom(hw);
+            return -E1000_ERR_EEPROM;
+        }
 
         e1000_standby_eeprom(hw);
 
@@ -3118,30 +3367,35 @@
         /* Send the READ command (opcode + addr)  */
         e1000_shift_out_ee_bits(hw, read_opcode, eeprom->opcode_bits);
         e1000_shift_out_ee_bits(hw, (uint16_t)(offset*2), eeprom->address_bits);
-    }
-    else if(eeprom->type == e1000_eeprom_microwire) {
-        /* Send the READ command (opcode + addr)  */
-        e1000_shift_out_ee_bits(hw, EEPROM_READ_OPCODE_MICROWIRE,
-                                eeprom->opcode_bits);
-        e1000_shift_out_ee_bits(hw, offset, eeprom->address_bits);
-    }
 
-    /* Read the data.  The address of the eeprom internally increments with
-     * each word (microwire) or byte (spi) being read, saving on the overhead
-     * of eeprom setup and tear-down.  The address counter will roll over if
-     * reading beyond the size of the eeprom, thus allowing the entire memory
-     * to be read starting from any offset. */
-    for (i = 0; i < words; i++) {
-        uint16_t word_in = e1000_shift_in_ee_bits(hw, 16);
-        if (eeprom->type == e1000_eeprom_spi)
-            word_in = (word_in >> 8) | (word_in << 8);
-        data[i] = word_in;
+        /* Read the data.  The address of the eeprom internally increments with
+         * each byte (spi) being read, saving on the overhead of eeprom setup
+         * and tear-down.  The address counter will roll over if reading beyond
+         * the size of the eeprom, thus allowing the entire memory to be read
+         * starting from any offset. */
+        for (i = 0; i < words; i++) {
+            word_in = e1000_shift_in_ee_bits(hw, 16);
+            data[i] = (word_in >> 8) | (word_in << 8);
+        }
+    } else if(eeprom->type == e1000_eeprom_microwire) {
+        for (i = 0; i < words; i++) {
+            /* Send the READ command (opcode + addr)  */
+            e1000_shift_out_ee_bits(hw, EEPROM_READ_OPCODE_MICROWIRE,
+                                    eeprom->opcode_bits);
+            e1000_shift_out_ee_bits(hw, (uint16_t)(offset + i),
+                                    eeprom->address_bits);
+
+            /* Read the data.  For microwire, each word requires the overhead
+             * of eeprom setup and tear-down. */
+            data[i] = e1000_shift_in_ee_bits(hw, 16);
+            e1000_standby_eeprom(hw);
+        }
     }
 
     /* End this read operation */
     e1000_release_eeprom(hw);
 
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -3169,9 +3423,9 @@
         checksum += eeprom_data;
     }
 
-    if(checksum == (uint16_t) EEPROM_SUM) {
-        return 0;
-    } else {
+    if(checksum == (uint16_t) EEPROM_SUM)
+        return E1000_SUCCESS;
+    else {
         DEBUGOUT("EEPROM Checksum Invalid\n");
         return -E1000_ERR_EEPROM;
     }
@@ -3205,7 +3459,7 @@
         DEBUGOUT("EEPROM Write Error\n");
         return -E1000_ERR_EEPROM;
     }
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -3403,7 +3657,7 @@
 
     e1000_shift_out_ee_bits(hw, 0, (uint16_t)(eeprom->address_bits - 2));
 
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -3437,7 +3691,7 @@
     /* Save word 1 in lower half of part_num */
     *part_num |= eeprom_data;
 
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -3463,7 +3717,7 @@
         hw->perm_mac_addr[i] = (uint8_t) (eeprom_data & 0x00FF);
         hw->perm_mac_addr[i+1] = (uint8_t) (eeprom_data >> 8);
     }
-    if((hw->mac_type == e1000_82546) &&
+    if(((hw->mac_type == e1000_82546) || (hw->mac_type == e1000_82546_rev_3)) &&
        (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) {
         if(hw->perm_mac_addr[5] & 0x01)
             hw->perm_mac_addr[5] &= ~(0x01);
@@ -3472,7 +3726,7 @@
     }
     for(i = 0; i < NODE_ADDRESS_SIZE; i++)
         hw->mac_addr[i] = hw->perm_mac_addr[i];
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -3748,7 +4002,7 @@
 
     if(hw->mac_type < e1000_82540) {
         /* Nothing to do */
-        return 0;
+        return E1000_SUCCESS;
     }
 
     ledctl = E1000_READ_REG(hw, LEDCTL);
@@ -3799,7 +4053,7 @@
             break;
         }
     }
-    return 0;
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -3811,49 +4065,48 @@
 e1000_setup_led(struct e1000_hw *hw)
 {
     uint32_t ledctl;
+    int32_t ret_val = E1000_SUCCESS;
 
     DEBUGFUNC("e1000_setup_led");
 
-    switch(hw->device_id) {
-    case E1000_DEV_ID_82542:
-    case E1000_DEV_ID_82543GC_FIBER:
-    case E1000_DEV_ID_82543GC_COPPER:
-    case E1000_DEV_ID_82544EI_COPPER:
-    case E1000_DEV_ID_82544EI_FIBER:
-    case E1000_DEV_ID_82544GC_COPPER:
-    case E1000_DEV_ID_82544GC_LOM:
+    switch(hw->mac_type) {
+    case e1000_82542_rev2_0:
+    case e1000_82542_rev2_1:
+    case e1000_82543:
+    case e1000_82544:
         /* No setup necessary */
         break;
-    case E1000_DEV_ID_82545EM_FIBER:
-    case E1000_DEV_ID_82546EB_FIBER:
-        ledctl = E1000_READ_REG(hw, LEDCTL);
-        /* Save current LEDCTL settings */
-        hw->ledctl_default = ledctl;
-        /* Turn off LED0 */
-        ledctl &= ~(E1000_LEDCTL_LED0_IVRT |
-                    E1000_LEDCTL_LED0_BLINK |
-                    E1000_LEDCTL_LED0_MODE_MASK);
-        ledctl |= (E1000_LEDCTL_MODE_LED_OFF << E1000_LEDCTL_LED0_MODE_SHIFT);
-        E1000_WRITE_REG(hw, LEDCTL, ledctl);
-        break;
-    case E1000_DEV_ID_82540EP:
-    case E1000_DEV_ID_82540EP_LOM:
-    case E1000_DEV_ID_82540EP_LP:
-    case E1000_DEV_ID_82540EM:
-    case E1000_DEV_ID_82540EM_LOM:
-    case E1000_DEV_ID_82545EM_COPPER:
-    case E1000_DEV_ID_82546EB_COPPER:
-    case E1000_DEV_ID_82546EB_QUAD_COPPER:
-    case E1000_DEV_ID_82541EI:
-    case E1000_DEV_ID_82541EP:
-    case E1000_DEV_ID_82547EI:
-        E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode1);
-        break;
+    case e1000_82541:
+    case e1000_82547:
+    case e1000_82541_rev_2:
+    case e1000_82547_rev_2:
+        /* Turn off PHY Smart Power Down (if enabled) */
+        if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO,
+                                         &hw->phy_spd_default)))
+            return ret_val;
+        if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO,
+                                          (uint16_t)(hw->phy_spd_default &
+                                          ~IGP01E1000_GMII_SPD))))
+            return ret_val;
+        /* Fall Through */
     default:
-        DEBUGOUT("Invalid device ID\n");
-        return -E1000_ERR_CONFIG;
+        if(hw->media_type == e1000_media_type_fiber) {
+            ledctl = E1000_READ_REG(hw, LEDCTL);
+            /* Save current LEDCTL settings */
+            hw->ledctl_default = ledctl;
+            /* Turn off LED0 */
+            ledctl &= ~(E1000_LEDCTL_LED0_IVRT |
+                        E1000_LEDCTL_LED0_BLINK |
+                        E1000_LEDCTL_LED0_MODE_MASK);
+            ledctl |= (E1000_LEDCTL_MODE_LED_OFF <<
+                       E1000_LEDCTL_LED0_MODE_SHIFT);
+            E1000_WRITE_REG(hw, LEDCTL, ledctl);
+        } else if(hw->media_type == e1000_media_type_copper)
+            E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode1);
+        break;
     }
-    return 0;
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -3864,39 +4117,33 @@
 int32_t
 e1000_cleanup_led(struct e1000_hw *hw)
 {
+    int32_t ret_val = E1000_SUCCESS;
+
     DEBUGFUNC("e1000_cleanup_led");
 
-    switch(hw->device_id) {
-    case E1000_DEV_ID_82542:
-    case E1000_DEV_ID_82543GC_FIBER:
-    case E1000_DEV_ID_82543GC_COPPER:
-    case E1000_DEV_ID_82544EI_COPPER:
-    case E1000_DEV_ID_82544EI_FIBER:
-    case E1000_DEV_ID_82544GC_COPPER:
-    case E1000_DEV_ID_82544GC_LOM:
+    switch(hw->mac_type) {
+    case e1000_82542_rev2_0:
+    case e1000_82542_rev2_1:
+    case e1000_82543:
+    case e1000_82544:
         /* No cleanup necessary */
         break;
-    case E1000_DEV_ID_82540EP:
-    case E1000_DEV_ID_82540EP_LOM:
-    case E1000_DEV_ID_82540EP_LP:
-    case E1000_DEV_ID_82540EM:
-    case E1000_DEV_ID_82540EM_LOM:
-    case E1000_DEV_ID_82545EM_COPPER:
-    case E1000_DEV_ID_82545EM_FIBER:
-    case E1000_DEV_ID_82546EB_COPPER:
-    case E1000_DEV_ID_82546EB_FIBER:
-    case E1000_DEV_ID_82546EB_QUAD_COPPER:
-    case E1000_DEV_ID_82541EI:
-    case E1000_DEV_ID_82541EP:
-    case E1000_DEV_ID_82547EI:
+    case e1000_82541:
+    case e1000_82547:
+    case e1000_82541_rev_2:
+    case e1000_82547_rev_2:
+        /* Turn on PHY Smart Power Down (if previously enabled) */
+        if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO,
+                                          hw->phy_spd_default)))
+            return ret_val;
+        /* Fall Through */
+    default:
         /* Restore LEDCTL settings */
         E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_default);
         break;
-    default:
-        DEBUGOUT("Invalid device ID\n");
-        return -E1000_ERR_CONFIG;
     }
-    return 0;
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -3907,50 +4154,44 @@
 int32_t
 e1000_led_on(struct e1000_hw *hw)
 {
-    uint32_t ctrl;
+    uint32_t ctrl = E1000_READ_REG(hw, CTRL);
 
     DEBUGFUNC("e1000_led_on");
 
-    switch(hw->device_id) {
-    case E1000_DEV_ID_82542:
-    case E1000_DEV_ID_82543GC_FIBER:
-    case E1000_DEV_ID_82543GC_COPPER:
-    case E1000_DEV_ID_82544EI_FIBER:
-        ctrl = E1000_READ_REG(hw, CTRL);
+    switch(hw->mac_type) {
+    case e1000_82542_rev2_0:
+    case e1000_82542_rev2_1:
+    case e1000_82543:
         /* Set SW Defineable Pin 0 to turn on the LED */
         ctrl |= E1000_CTRL_SWDPIN0;
         ctrl |= E1000_CTRL_SWDPIO0;
-        E1000_WRITE_REG(hw, CTRL, ctrl);
         break;
-    case E1000_DEV_ID_82544EI_COPPER:
-    case E1000_DEV_ID_82544GC_COPPER:
-    case E1000_DEV_ID_82544GC_LOM:
-    case E1000_DEV_ID_82545EM_FIBER:
-    case E1000_DEV_ID_82546EB_FIBER:
-        ctrl = E1000_READ_REG(hw, CTRL);
-        /* Clear SW Defineable Pin 0 to turn on the LED */
-        ctrl &= ~E1000_CTRL_SWDPIN0;
-        ctrl |= E1000_CTRL_SWDPIO0;
-        E1000_WRITE_REG(hw, CTRL, ctrl);
-        break;
-    case E1000_DEV_ID_82540EP:
-    case E1000_DEV_ID_82540EP_LOM:
-    case E1000_DEV_ID_82540EP_LP:
-    case E1000_DEV_ID_82540EM:
-    case E1000_DEV_ID_82540EM_LOM:
-    case E1000_DEV_ID_82545EM_COPPER:
-    case E1000_DEV_ID_82546EB_COPPER:
-    case E1000_DEV_ID_82546EB_QUAD_COPPER:
-    case E1000_DEV_ID_82541EI:
-    case E1000_DEV_ID_82541EP:
-    case E1000_DEV_ID_82547EI:
-        E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode2);
+    case e1000_82544:
+        if(hw->media_type == e1000_media_type_fiber) {
+            /* Set SW Defineable Pin 0 to turn on the LED */
+            ctrl |= E1000_CTRL_SWDPIN0;
+            ctrl |= E1000_CTRL_SWDPIO0;
+        } else {
+            /* Clear SW Defineable Pin 0 to turn on the LED */
+            ctrl &= ~E1000_CTRL_SWDPIN0;
+            ctrl |= E1000_CTRL_SWDPIO0;
+        }
         break;
     default:
-        DEBUGOUT("Invalid device ID\n");
-        return -E1000_ERR_CONFIG;
+        if(hw->media_type == e1000_media_type_fiber) {
+            /* Clear SW Defineable Pin 0 to turn on the LED */
+            ctrl &= ~E1000_CTRL_SWDPIN0;
+            ctrl |= E1000_CTRL_SWDPIO0;
+        } else if(hw->media_type == e1000_media_type_copper) {
+            E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode2);
+            return E1000_SUCCESS;
+        }
+        break;
     }
-    return 0;
+
+    E1000_WRITE_REG(hw, CTRL, ctrl);
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -3961,50 +4202,44 @@
 int32_t
 e1000_led_off(struct e1000_hw *hw)
 {
-    uint32_t ctrl;
+    uint32_t ctrl = E1000_READ_REG(hw, CTRL);
 
     DEBUGFUNC("e1000_led_off");
 
-    switch(hw->device_id) {
-    case E1000_DEV_ID_82542:
-    case E1000_DEV_ID_82543GC_FIBER:
-    case E1000_DEV_ID_82543GC_COPPER:
-    case E1000_DEV_ID_82544EI_FIBER:
-        ctrl = E1000_READ_REG(hw, CTRL);
+    switch(hw->mac_type) {
+    case e1000_82542_rev2_0:
+    case e1000_82542_rev2_1:
+    case e1000_82543:
         /* Clear SW Defineable Pin 0 to turn off the LED */
         ctrl &= ~E1000_CTRL_SWDPIN0;
         ctrl |= E1000_CTRL_SWDPIO0;
-        E1000_WRITE_REG(hw, CTRL, ctrl);
         break;
-    case E1000_DEV_ID_82544EI_COPPER:
-    case E1000_DEV_ID_82544GC_COPPER:
-    case E1000_DEV_ID_82544GC_LOM:
-    case E1000_DEV_ID_82545EM_FIBER:
-    case E1000_DEV_ID_82546EB_FIBER:
-        ctrl = E1000_READ_REG(hw, CTRL);
-        /* Set SW Defineable Pin 0 to turn off the LED */
-        ctrl |= E1000_CTRL_SWDPIN0;
-        ctrl |= E1000_CTRL_SWDPIO0;
-        E1000_WRITE_REG(hw, CTRL, ctrl);
-        break;
-    case E1000_DEV_ID_82540EP:
-    case E1000_DEV_ID_82540EP_LOM:
-    case E1000_DEV_ID_82540EP_LP:
-    case E1000_DEV_ID_82540EM:
-    case E1000_DEV_ID_82540EM_LOM:
-    case E1000_DEV_ID_82545EM_COPPER:
-    case E1000_DEV_ID_82546EB_COPPER:
-    case E1000_DEV_ID_82546EB_QUAD_COPPER:
-    case E1000_DEV_ID_82541EI:
-    case E1000_DEV_ID_82541EP:
-    case E1000_DEV_ID_82547EI:
-        E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode1);
+    case e1000_82544:
+        if(hw->media_type == e1000_media_type_fiber) {
+            /* Clear SW Defineable Pin 0 to turn off the LED */
+            ctrl &= ~E1000_CTRL_SWDPIN0;
+            ctrl |= E1000_CTRL_SWDPIO0;
+        } else {
+            /* Set SW Defineable Pin 0 to turn off the LED */
+            ctrl |= E1000_CTRL_SWDPIN0;
+            ctrl |= E1000_CTRL_SWDPIO0;
+        }
         break;
     default:
-        DEBUGOUT("Invalid device ID\n");
-        return -E1000_ERR_CONFIG;
+        if(hw->media_type == e1000_media_type_fiber) {
+            /* Set SW Defineable Pin 0 to turn off the LED */
+            ctrl |= E1000_CTRL_SWDPIN0;
+            ctrl |= E1000_CTRL_SWDPIO0;
+        } else if(hw->media_type == e1000_media_type_copper) {
+            E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode1);
+            return E1000_SUCCESS;
+        }
+        break;
     }
-    return 0;
+
+    E1000_WRITE_REG(hw, CTRL, ctrl);
+
+    return E1000_SUCCESS;
 }
 
 /******************************************************************************
@@ -4127,8 +4362,7 @@
     DEBUGFUNC("e1000_update_adaptive");
 
     if(hw->adaptive_ifs) {
-        if((hw->collision_delta * hw->ifs_ratio) >
-           hw->tx_packet_delta) {
+        if((hw->collision_delta * hw->ifs_ratio) > hw->tx_packet_delta) {
             if(hw->tx_packet_delta > MIN_NUM_XMITS) {
                 hw->in_ifs_mode = TRUE;
                 if(hw->current_ifs_val < hw->ifs_max_val) {
@@ -4140,8 +4374,7 @@
                 }
             }
         } else {
-            if((hw->in_ifs_mode == TRUE) &&
-               (hw->tx_packet_delta <= MIN_NUM_XMITS)) {
+            if(hw->in_ifs_mode && (hw->tx_packet_delta <= MIN_NUM_XMITS)) {
                 hw->current_ifs_val = 0;
                 hw->in_ifs_mode = FALSE;
                 E1000_WRITE_REG(hw, AIT, 0);
@@ -4324,7 +4557,8 @@
  * min_length - The estimated minimum length
  * max_length - The estimated maximum length
  *
- * returns: E1000_SUCCESS / -E1000_ERR_XXX
+ * returns: - E1000_ERR_XXX
+ *            E1000_SUCCESS
  *
  * This function always returns a ranged length (minimum & maximum).
  * So for M88 phy's, this function interprets the one value returned from the
@@ -4332,9 +4566,11 @@
  * For IGP phy's, the function calculates the range by the AGC registers.
  *****************************************************************************/
 int32_t
-e1000_get_cable_length(struct e1000_hw *hw, uint16_t *min_length,
+e1000_get_cable_length(struct e1000_hw *hw,
+                       uint16_t *min_length,
                        uint16_t *max_length)
 {
+    int32_t ret_val;
     uint16_t agc_value = 0;
     uint16_t cur_agc, min_agc = IGP01E1000_AGC_LENGTH_TABLE_SIZE;
     uint16_t i, phy_data;
@@ -4345,8 +4581,9 @@
 
     /* Use old method for Phy older than IGP */
     if(hw->phy_type == e1000_phy_m88) {
-        if(e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data) < 0)
-            return -E1000_ERR_PHY;
+        if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS,
+                                         &phy_data)))
+            return ret_val;
 
         /* Convert the enum value to ranged values */
         switch((phy_data & M88E1000_PSSR_CABLE_LENGTH) >>
@@ -4376,19 +4613,16 @@
             break;
         }
     } else if(hw->phy_type == e1000_phy_igp) { /* For IGP PHY */
-        uint16_t agc_reg_array[IGP01E1000_PHY_AGC_NUM] = {IGP01E1000_PHY_AGC_A,
+        uint16_t agc_reg_array[IGP01E1000_PHY_CHANNEL_NUM] =
+                                                         {IGP01E1000_PHY_AGC_A,
                                                           IGP01E1000_PHY_AGC_B,
                                                           IGP01E1000_PHY_AGC_C,
                                                           IGP01E1000_PHY_AGC_D};
         /* Read the AGC registers for all channels */
-        for(i = 0; i < IGP01E1000_PHY_AGC_NUM; i++) {
-            if(e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT,
-                                   agc_reg_array[i]) != E1000_SUCCESS)
-                return -E1000_ERR_PHY;
-            if(e1000_read_phy_reg(hw, agc_reg_array[i] &
-                                  IGP01E1000_PHY_PAGE_SELECT, &phy_data) !=
-                                  E1000_SUCCESS)
-                return -E1000_ERR_PHY;
+        for(i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) {
+
+            if((ret_val = e1000_read_phy_reg(hw, agc_reg_array[i], &phy_data)))
+                return ret_val;
 
             cur_agc = phy_data >> IGP01E1000_AGC_LENGTH_SHIFT;
 
@@ -4404,20 +4638,15 @@
                 min_agc = cur_agc;
         }
 
-        /* Return to page 0 */
-        if(e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT, 0x0) !=
-           E1000_SUCCESS)
-            return -E1000_ERR_PHY;
-
         /* Remove the minimal AGC result for length < 50m */
-        if(agc_value < IGP01E1000_PHY_AGC_NUM * e1000_igp_cable_length_50) {
+        if(agc_value < IGP01E1000_PHY_CHANNEL_NUM * e1000_igp_cable_length_50) {
             agc_value -= min_agc;
 
             /* Get the average length of the remaining 3 channels */
-            agc_value /= (IGP01E1000_PHY_AGC_NUM - 1);
+            agc_value /= (IGP01E1000_PHY_CHANNEL_NUM - 1);
         } else {
             /* Get the average length of all the 4 channels. */
-            agc_value /= IGP01E1000_PHY_AGC_NUM;
+            agc_value /= IGP01E1000_PHY_CHANNEL_NUM;
         }
 
         /* Set the range of the calculated length. */
@@ -4439,7 +4668,8 @@
  * polarity - output parameter : 0 - Polarity is not reversed
  *                               1 - Polarity is reversed.
  *
- * returns: E1000_SUCCESS / -E1000_ERR_XXX
+ * returns: - E1000_ERR_XXX
+ *            E1000_SUCCESS
  *
  * For phy's older then IGP, this function simply reads the polarity bit in the
  * Phy Status register.  For IGP phy's, this bit is valid only if link speed is
@@ -4448,22 +4678,26 @@
  * IGP01E1000_PHY_PCS_INIT_REG.
  *****************************************************************************/
 int32_t
-e1000_check_polarity(struct e1000_hw *hw, uint16_t *polarity)
+e1000_check_polarity(struct e1000_hw *hw,
+                     uint16_t *polarity)
 {
+    int32_t ret_val;
     uint16_t phy_data;
 
     DEBUGFUNC("e1000_check_polarity");
 
     if(hw->phy_type == e1000_phy_m88) {
         /* return the Polarity bit in the Status register. */
-        if(e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data) < 0)
-            return -E1000_ERR_PHY;
+        if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS,
+                                         &phy_data)))
+            return ret_val;
         *polarity = (phy_data & M88E1000_PSSR_REV_POLARITY) >>
                     M88E1000_PSSR_REV_POLARITY_SHIFT;
     } else if(hw->phy_type == e1000_phy_igp) {
         /* Read the Status register to check the speed */
-        if(e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, &phy_data) < 0)
-            return -E1000_ERR_PHY;
+        if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS,
+                                         &phy_data)))
+            return ret_val;
 
         /* If speed is 1000 Mbps, must read the IGP01E1000_PHY_PCS_INIT_REG to
          * find the polarity status */
@@ -4471,18 +4705,9 @@
            IGP01E1000_PSSR_SPEED_1000MBPS) {
 
             /* Read the GIG initialization PCS register (0x00B4) */
-            if(e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT,
-                                   IGP01E1000_PHY_PCS_INIT_REG) < 0)
-                return -E1000_ERR_PHY;
-
-            if(e1000_read_phy_reg(hw, IGP01E1000_PHY_PCS_INIT_REG &
-                                  IGP01E1000_PHY_PAGE_SELECT, &phy_data) < 0)
-                return -E1000_ERR_PHY;
-
-            /* Return to page 0 */
-            if(e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT, 0x0) !=
-               E1000_SUCCESS)
-                return -E1000_ERR_PHY;
+            if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PCS_INIT_REG,
+                                             &phy_data)))
+                return ret_val;
 
             /* Check the polarity bits */
             *polarity = (phy_data & IGP01E1000_PHY_POLARITY_MASK) ? 1 : 0;
@@ -4502,7 +4727,8 @@
  * downshift - output parameter : 0 - No Downshift ocured.
  *                                1 - Downshift ocured.
  *
- * returns: E1000_SUCCESS / -E1000_ERR_XXX
+ * returns: - E1000_ERR_XXX
+ *            E1000_SUCCESS 
  *
  * For phy's older then IGP, this function reads the Downshift bit in the Phy
  * Specific Status register.  For IGP phy's, it reads the Downgrade bit in the
@@ -4512,25 +4738,287 @@
 int32_t
 e1000_check_downshift(struct e1000_hw *hw)
 {
+    int32_t ret_val;
     uint16_t phy_data;
 
     DEBUGFUNC("e1000_check_downshift");
 
     if(hw->phy_type == e1000_phy_igp) {
-        if(e1000_read_phy_reg(hw, IGP01E1000_PHY_LINK_HEALTH, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_LINK_HEALTH,
+                                         &phy_data)))
+            return ret_val;
+
         hw->speed_downgraded = (phy_data & IGP01E1000_PLHR_SS_DOWNGRADE) ? 1 : 0;
     }
     else if(hw->phy_type == e1000_phy_m88) {
-        if(e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data) < 0) {
-            DEBUGOUT("PHY Read Error\n");
-            return -E1000_ERR_PHY;
-        }
+        if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS,
+                                         &phy_data)))
+            return ret_val;
+
         hw->speed_downgraded = (phy_data & M88E1000_PSSR_DOWNSHIFT) >>
-                                M88E1000_PSSR_DOWNSHIFT_SHIFT;
+                               M88E1000_PSSR_DOWNSHIFT_SHIFT;
     }
     return E1000_SUCCESS;
 }
 
+/*****************************************************************************
+ *
+ * 82541_rev_2 & 82547_rev_2 have the capability to configure the DSP when a
+ * gigabit link is achieved to improve link quality.
+ *
+ * hw: Struct containing variables accessed by shared code
+ *
+ * returns: - E1000_ERR_PHY if fail to read/write the PHY
+ *            E1000_SUCCESS at any other case.
+ *
+ ****************************************************************************/
+
+int32_t
+e1000_config_dsp_after_link_change(struct e1000_hw *hw,
+                                   boolean_t link_up)
+{
+    int32_t ret_val;
+    uint16_t phy_data, speed, duplex, i;
+    uint16_t dsp_reg_array[IGP01E1000_PHY_CHANNEL_NUM] =
+                                        {IGP01E1000_PHY_AGC_PARAM_A,
+                                        IGP01E1000_PHY_AGC_PARAM_B,
+                                        IGP01E1000_PHY_AGC_PARAM_C,
+                                        IGP01E1000_PHY_AGC_PARAM_D};
+    uint16_t min_length, max_length;
+
+    DEBUGFUNC("e1000_config_dsp_after_link_change");
+
+    if(hw->phy_type != e1000_phy_igp)
+        return E1000_SUCCESS;
+
+    if(link_up) {
+        if((ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex))) {
+            DEBUGOUT("Error getting link speed and duplex\n");
+            return ret_val;
+        }
+
+        if(speed == SPEED_1000) {
+
+            e1000_get_cable_length(hw, &min_length, &max_length);
+
+            if((hw->dsp_config_state == e1000_dsp_config_enabled) &&
+                min_length >= e1000_igp_cable_length_50) {
+
+                for(i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) {
+                    if((ret_val = e1000_read_phy_reg(hw, dsp_reg_array[i],
+                                                     &phy_data)))
+                        return ret_val;
+
+                    phy_data &= ~IGP01E1000_PHY_EDAC_MU_INDEX;
+
+                    if((ret_val = e1000_write_phy_reg(hw, dsp_reg_array[i],
+                                                      phy_data)))
+                        return ret_val;
+                }
+                hw->dsp_config_state = e1000_dsp_config_activated;
+            }
+
+            if((hw->ffe_config_state == e1000_ffe_config_enabled) &&
+               (min_length < e1000_igp_cable_length_50)) {
+
+                uint16_t ffe_idle_err_timeout = FFE_IDLE_ERR_COUNT_TIMEOUT_20;
+                uint32_t idle_errs = 0;
+
+                /* clear previous idle error counts */
+                if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS,
+                                                 &phy_data)))
+                    return ret_val;
+
+                for(i = 0; i < ffe_idle_err_timeout; i++) {
+                    udelay(1000);
+                    if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS,
+                                                     &phy_data)))
+                        return ret_val;
+
+                    idle_errs += (phy_data & SR_1000T_IDLE_ERROR_CNT);
+                    if(idle_errs > SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT) {
+                        hw->ffe_config_state = e1000_ffe_config_active;
+
+                        if((ret_val = e1000_write_phy_reg(hw,
+                                    IGP01E1000_PHY_DSP_FFE,
+                                    IGP01E1000_PHY_DSP_FFE_CM_CP)))
+                            return ret_val;
+                        break;
+                    }
+
+                    if(idle_errs)
+                        ffe_idle_err_timeout = FFE_IDLE_ERR_COUNT_TIMEOUT_100;
+                }
+            }
+        }
+    } else {
+        if(hw->dsp_config_state == e1000_dsp_config_activated) {
+            if((ret_val = e1000_write_phy_reg(hw, 0x0000,
+                IGP01E1000_IEEE_FORCE_GIGA)))
+                return ret_val;
+            for(i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) {
+                if((ret_val = e1000_read_phy_reg(hw, dsp_reg_array[i],
+                                                 &phy_data)))
+                    return ret_val;
+
+                phy_data &= ~IGP01E1000_PHY_EDAC_MU_INDEX;
+                phy_data |=  IGP01E1000_PHY_EDAC_SIGN_EXT_9_BITS;
+
+                if((ret_val = e1000_write_phy_reg(hw,dsp_reg_array[i],
+                                                  phy_data)))
+                    return ret_val;
+            }
+
+            if((ret_val = e1000_write_phy_reg(hw, 0x0000,
+                                              IGP01E1000_IEEE_RESTART_AUTONEG)))
+                return ret_val;
+
+            hw->dsp_config_state = e1000_dsp_config_enabled;
+        }
+
+        if(hw->ffe_config_state == e1000_ffe_config_active) {
+            if((ret_val = e1000_write_phy_reg(hw, 0x0000,
+                                              IGP01E1000_IEEE_FORCE_GIGA)))
+                return ret_val;
+            if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_DSP_FFE,
+                                              IGP01E1000_PHY_DSP_FFE_DEFAULT)))
+                return ret_val;
+
+            if((ret_val = e1000_write_phy_reg(hw, 0x0000,
+                                              IGP01E1000_IEEE_RESTART_AUTONEG)))
+                return ret_val;
+        hw->ffe_config_state = e1000_ffe_config_enabled;
+        }
+    }
+    return E1000_SUCCESS;
+}
+
+/*****************************************************************************
+ *
+ * This function sets the lplu state according to the active flag.  When
+ * activating lplu this function also disables smart speed and vise versa.
+ * lplu will not be activated unless the device autonegotiation advertisment
+ * meets standards of either 10 or 10/100 or 10/100/1000 at all duplexes.
+ * hw: Struct containing variables accessed by shared code
+ * active - true to enable lplu false to disable lplu.
+ *
+ * returns: - E1000_ERR_PHY if fail to read/write the PHY
+ *            E1000_SUCCESS at any other case.
+ *
+ ****************************************************************************/
+
+int32_t
+e1000_set_d3_lplu_state(struct e1000_hw *hw,
+                        boolean_t active)
+{
+    int32_t ret_val;
+    uint16_t phy_data;
+    DEBUGFUNC("e1000_set_d3_lplu_state");
+
+    if(!((hw->mac_type == e1000_82541_rev_2) ||
+         (hw->mac_type == e1000_82547_rev_2)))
+        return E1000_SUCCESS;
+
+    /* During driver activity LPLU should not be used or it will attain link
+     * from the lowest speeds starting from 10Mbps. The capability is used for
+     * Dx transitions and states */
+    if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, &phy_data)))
+        return ret_val;
+
+    if(!active) {
+        phy_data &= ~IGP01E1000_GMII_FLEX_SPD;
+        if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, phy_data)))
+            return ret_val;
+
+        /* LPLU and SmartSpeed are mutually exclusive.  LPLU is used during
+         * Dx states where the power conservation is most important.  During
+         * driver activity we should enable SmartSpeed, so performance is
+         * maintained. */
+        if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+                                         &phy_data)))
+            return ret_val;
+
+        phy_data |= IGP01E1000_PSCFR_SMART_SPEED;
+        if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+                                          phy_data)))
+            return ret_val;
+
+    } else if((hw->autoneg_advertised == AUTONEG_ADVERTISE_SPEED_DEFAULT) ||
+              (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_ALL ) ||
+              (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_100_ALL)) {
+
+        phy_data |= IGP01E1000_GMII_FLEX_SPD;
+        if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, phy_data)))
+            return ret_val;
+
+        /* When LPLU is enabled we should disable SmartSpeed */
+        if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+                                         &phy_data)))
+            return ret_val;
+
+        phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED;
+        if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+                                          phy_data)))
+            return ret_val;
+
+    }
+    return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Change VCO speed register to improve Bit Error Rate performance of SERDES.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static int32_t
+e1000_set_vco_speed(struct e1000_hw *hw)
+{
+    int32_t  ret_val;
+    uint16_t default_page = 0;
+    uint16_t phy_data;
+
+    DEBUGFUNC("e1000_set_vco_speed");
+
+    switch(hw->mac_type) {
+    case e1000_82545_rev_3:
+    case e1000_82546_rev_3:
+       break;
+    default:
+        return E1000_SUCCESS;
+    }
+
+    /* Set PHY register 30, page 5, bit 8 to 0 */
+
+    if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_PAGE_SELECT,
+                                     &default_page)))
+        return ret_val;
+
+    if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0005)))
+        return ret_val;
+
+    if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data)))
+        return ret_val;
+
+    phy_data &= ~M88E1000_PHY_VCO_REG_BIT8;
+    if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data)))
+        return ret_val;
+
+    /* Set PHY register 30, page 4, bit 11 to 1 */
+
+    if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0004)))
+        return ret_val;
+
+    if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data)))
+        return ret_val;
+
+    phy_data |= M88E1000_PHY_VCO_REG_BIT11;
+    if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data)))
+        return ret_val;
+
+    if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT,
+                                      default_page)))
+        return ret_val;
+
+    return E1000_SUCCESS;
+}
+
diff -urN linux-2.4.23-pre5/drivers/net/e1000/e1000_hw.h linux-2.4.23-pre6/drivers/net/e1000/e1000_hw.h
--- linux-2.4.23-pre5/drivers/net/e1000/e1000_hw.h	2003-08-25 04:44:42.000000000 -0700
+++ linux-2.4.23-pre6/drivers/net/e1000/e1000_hw.h	2003-10-01 10:38:50.000000000 -0700
@@ -33,9 +33,9 @@
 #ifndef _E1000_HW_H_
 #define _E1000_HW_H_
 
-
 #include "e1000_osdep.h"
 
+
 /* Forward declarations of structures used by the shared code */
 struct e1000_hw;
 struct e1000_hw_stats;
@@ -50,9 +50,13 @@
     e1000_82544,
     e1000_82540,
     e1000_82545,
+    e1000_82545_rev_3,
     e1000_82546,
+    e1000_82546_rev_3,
     e1000_82541,
+    e1000_82541_rev_2,
     e1000_82547,
+    e1000_82547_rev_2,
     e1000_num_macs
 } e1000_mac_type;
 
@@ -67,6 +71,7 @@
 typedef enum {
     e1000_media_type_copper = 0,
     e1000_media_type_fiber = 1,
+    e1000_media_type_internal_serdes = 2,
     e1000_num_media_types
 } e1000_media_type;
 
@@ -90,7 +95,8 @@
 typedef enum {
     e1000_bus_type_unknown = 0,
     e1000_bus_type_pci,
-    e1000_bus_type_pcix
+    e1000_bus_type_pcix,
+    e1000_bus_type_reserved
 } e1000_bus_type;
 
 /* PCI bus speeds */
@@ -108,7 +114,8 @@
 typedef enum {
     e1000_bus_width_unknown = 0,
     e1000_bus_width_32,
-    e1000_bus_width_64
+    e1000_bus_width_64,
+    e1000_bus_width_reserved
 } e1000_bus_width;
 
 /* PHY status info structure and supporting enums */
@@ -186,6 +193,26 @@
     e1000_phy_undefined = 0xFF
 } e1000_phy_type;
 
+typedef enum {
+    e1000_ms_hw_default = 0,
+    e1000_ms_force_master,
+    e1000_ms_force_slave,
+    e1000_ms_auto
+} e1000_ms_type;
+
+typedef enum {
+    e1000_ffe_config_enabled = 0,
+    e1000_ffe_config_active,
+    e1000_ffe_config_blocked
+} e1000_ffe_config;
+
+typedef enum {
+    e1000_dsp_config_disabled = 0,
+    e1000_dsp_config_enabled,
+    e1000_dsp_config_activated,
+    e1000_dsp_config_undefined = 0xFF
+} e1000_dsp_config;
+
 struct e1000_phy_info {
     e1000_cable_length cable_length;
     e1000_10bt_ext_dist_enable extended_10bt_distance;
@@ -224,9 +251,10 @@
 
 /* Function prototypes */
 /* Initialization */
-void e1000_reset_hw(struct e1000_hw *hw);
+int32_t e1000_reset_hw(struct e1000_hw *hw);
 int32_t e1000_init_hw(struct e1000_hw *hw);
 int32_t e1000_set_mac_type(struct e1000_hw *hw);
+void e1000_set_media_type(struct e1000_hw *hw);
 
 /* Link Configuration */
 int32_t e1000_setup_link(struct e1000_hw *hw);
@@ -234,8 +262,9 @@
 void e1000_config_collision_dist(struct e1000_hw *hw);
 int32_t e1000_config_fc_after_link_up(struct e1000_hw *hw);
 int32_t e1000_check_for_link(struct e1000_hw *hw);
-void e1000_get_speed_and_duplex(struct e1000_hw *hw, uint16_t * speed, uint16_t * duplex);
+int32_t e1000_get_speed_and_duplex(struct e1000_hw *hw, uint16_t * speed, uint16_t * duplex);
 int32_t e1000_wait_autoneg(struct e1000_hw *hw);
+int32_t e1000_force_mac_fc(struct e1000_hw *hw);
 
 /* PHY */
 int32_t e1000_read_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *phy_data);
@@ -292,6 +321,8 @@
 uint32_t e1000_read_reg_io(struct e1000_hw *hw, uint32_t offset);
 void e1000_io_write(struct e1000_hw *hw, uint32_t port, uint32_t value);
 void e1000_write_reg_io(struct e1000_hw *hw, uint32_t offset, uint32_t value);
+int32_t e1000_config_dsp_after_link_change(struct e1000_hw *hw, boolean_t link_up);
+int32_t e1000_set_d3_lplu_state(struct e1000_hw *hw, boolean_t active);
 
 #define E1000_READ_REG_IO(a, reg) \
     e1000_read_reg_io((a), E1000_##reg)
@@ -313,13 +344,22 @@
 #define E1000_DEV_ID_82540EP_LP          0x101E
 #define E1000_DEV_ID_82545EM_COPPER      0x100F
 #define E1000_DEV_ID_82545EM_FIBER       0x1011
+#define E1000_DEV_ID_82545GM_COPPER      0x1026
+#define E1000_DEV_ID_82545GM_FIBER       0x1027
+#define E1000_DEV_ID_82545GM_SERDES      0x1028
 #define E1000_DEV_ID_82546EB_COPPER      0x1010
 #define E1000_DEV_ID_82546EB_FIBER       0x1012
 #define E1000_DEV_ID_82546EB_QUAD_COPPER 0x101D
 #define E1000_DEV_ID_82541EI             0x1013
-#define E1000_DEV_ID_82541EP             0x1018
+#define E1000_DEV_ID_82541EI_MOBILE      0x1018
+#define E1000_DEV_ID_82541ER             0x1078
+#define E1000_DEV_ID_82547GI             0x1075
+#define E1000_DEV_ID_82541GI             0x1076
+#define E1000_DEV_ID_82541GI_MOBILE      0x1077
+#define E1000_DEV_ID_82546GB_COPPER      0x1079
+#define E1000_DEV_ID_82546GB_FIBER       0x107A
+#define E1000_DEV_ID_82546GB_SERDES      0x107B
 #define E1000_DEV_ID_82547EI             0x1019
-#define NUM_DEV_IDS 20
 
 #define NODE_ADDRESS_SIZE 6
 #define ETH_LENGTH_OF_ADDRESS 6
@@ -385,7 +425,7 @@
     E1000_IMS_RXSEQ  |    \
     E1000_IMS_LSC)
 
-/* The number of high/low register pairs in the RAR. The RAR (Receive Address
+/* Number of high/low register pairs in the RAR. The RAR (Receive Address
  * Registers) holds the directed and multicast addresses that we monitor. We
  * reserve one of these spots for our directed address, allowing us room for
  * E1000_RAR_ENTRIES - 1 multicast addresses.
@@ -539,7 +579,7 @@
     volatile uint32_t high;     /* receive address high */
 };
 
-/* The number of entries in the Multicast Table Array (MTA). */
+/* Number of entries in the Multicast Table Array (MTA). */
 #define E1000_NUM_MTA_REGISTERS 128
 
 /* IPv4 Address Table Entry */
@@ -599,6 +639,7 @@
  * A - register array
  */
 #define E1000_CTRL     0x00000  /* Device Control - RW */
+#define E1000_CTRL_DUP 0x00004  /* Device Control Duplicate (Shadow) - RW */
 #define E1000_STATUS   0x00008  /* Device Status - RO */
 #define E1000_EECD     0x00010  /* EEPROM/Flash Control - RW */
 #define E1000_EERD     0x00014  /* EEPROM Read - RW */
@@ -934,6 +975,9 @@
     e1000_bus_width bus_width;
     e1000_bus_type bus_type;
     struct e1000_eeprom_info eeprom;
+    e1000_ms_type master_slave;
+    e1000_ms_type original_master_slave;
+    e1000_ffe_config ffe_config_state;
     uint32_t io_base;
     uint32_t phy_id;
     uint32_t phy_revision;
@@ -950,6 +994,8 @@
     uint32_t ledctl_default;
     uint32_t ledctl_mode1;
     uint32_t ledctl_mode2;
+    uint16_t phy_spd_default;
+    uint16_t dsp_reset_counter;
     uint16_t autoneg_advertised;
     uint16_t pci_cmd_word;
     uint16_t fc_high_water;
@@ -974,10 +1020,13 @@
     uint8_t perm_mac_addr[NODE_ADDRESS_SIZE];
     boolean_t disable_polarity_correction;
     boolean_t speed_downgraded;
+    e1000_dsp_config dsp_config_state;
     boolean_t get_link_status;
     boolean_t tbi_compatibility_en;
     boolean_t tbi_compatibility_on;
+    boolean_t phy_reset_disable;
     boolean_t fc_send_xon;
+    boolean_t fc_strict_ieee;
     boolean_t report_tx_early;
     boolean_t adaptive_ifs;
     boolean_t ifs_params_forced;
@@ -1059,7 +1108,7 @@
 #define E1000_EECD_PRES      0x00000100 /* EEPROM Present */
 #define E1000_EECD_SIZE      0x00000200 /* EEPROM Size (0=64 word 1=256 word) */
 #define E1000_EECD_ADDR_BITS 0x00000400 /* EEPROM Addressing bits based on type
-					 * (0-small, 1-large) */
+                                         * (0-small, 1-large) */
 #define E1000_EECD_TYPE      0x00002000 /* EEPROM Type (1-SPI, 0-Microwire) */
 #ifndef E1000_EEPROM_GRANT_ATTEMPTS
 #define E1000_EEPROM_GRANT_ATTEMPTS 1000 /* EEPROM # attempts to gain grant */
@@ -1121,22 +1170,22 @@
 #define E1000_MDIC_ERROR     0x40000000
 
 /* LED Control */
-#define E1000_LEDCTL_LED0_MODE_MASK  0x0000000F
-#define E1000_LEDCTL_LED0_MODE_SHIFT 0
-#define E1000_LEDCTL_LED0_IVRT       0x00000040
-#define E1000_LEDCTL_LED0_BLINK      0x00000080
-#define E1000_LEDCTL_LED1_MODE_MASK  0x00000F00
-#define E1000_LEDCTL_LED1_MODE_SHIFT 8
-#define E1000_LEDCTL_LED1_IVRT       0x00004000
-#define E1000_LEDCTL_LED1_BLINK      0x00008000
-#define E1000_LEDCTL_LED2_MODE_MASK  0x000F0000
-#define E1000_LEDCTL_LED2_MODE_SHIFT 16
-#define E1000_LEDCTL_LED2_IVRT       0x00400000
-#define E1000_LEDCTL_LED2_BLINK      0x00800000
-#define E1000_LEDCTL_LED3_MODE_MASK  0x0F000000
-#define E1000_LEDCTL_LED3_MODE_SHIFT 24
-#define E1000_LEDCTL_LED3_IVRT       0x40000000
-#define E1000_LEDCTL_LED3_BLINK      0x80000000
+#define E1000_LEDCTL_LED0_MODE_MASK       0x0000000F
+#define E1000_LEDCTL_LED0_MODE_SHIFT      0
+#define E1000_LEDCTL_LED0_IVRT            0x00000040
+#define E1000_LEDCTL_LED0_BLINK           0x00000080
+#define E1000_LEDCTL_LED1_MODE_MASK       0x00000F00
+#define E1000_LEDCTL_LED1_MODE_SHIFT      8
+#define E1000_LEDCTL_LED1_IVRT            0x00004000
+#define E1000_LEDCTL_LED1_BLINK           0x00008000
+#define E1000_LEDCTL_LED2_MODE_MASK       0x000F0000
+#define E1000_LEDCTL_LED2_MODE_SHIFT      16
+#define E1000_LEDCTL_LED2_IVRT            0x00400000
+#define E1000_LEDCTL_LED2_BLINK           0x00800000
+#define E1000_LEDCTL_LED3_MODE_MASK       0x0F000000
+#define E1000_LEDCTL_LED3_MODE_SHIFT      24
+#define E1000_LEDCTL_LED3_IVRT            0x40000000
+#define E1000_LEDCTL_LED3_BLINK           0x80000000
 
 #define E1000_LEDCTL_MODE_LINK_10_1000  0x0
 #define E1000_LEDCTL_MODE_LINK_100_1000 0x1
@@ -1159,109 +1208,109 @@
 #define E1000_RAH_AV  0x80000000        /* Receive descriptor valid */
 
 /* Interrupt Cause Read */
-#define E1000_ICR_TXDW    0x00000001    /* Transmit desc written back */
-#define E1000_ICR_TXQE    0x00000002    /* Transmit Queue empty */
-#define E1000_ICR_LSC     0x00000004    /* Link Status Change */
-#define E1000_ICR_RXSEQ   0x00000008    /* rx sequence error */
-#define E1000_ICR_RXDMT0  0x00000010    /* rx desc min. threshold (0) */
-#define E1000_ICR_RXO     0x00000040    /* rx overrun */
-#define E1000_ICR_RXT0    0x00000080    /* rx timer intr (ring 0) */
-#define E1000_ICR_MDAC    0x00000200    /* MDIO access complete */
-#define E1000_ICR_RXCFG   0x00000400    /* RX /c/ ordered set */
-#define E1000_ICR_GPI_EN0 0x00000800    /* GP Int 0 */
-#define E1000_ICR_GPI_EN1 0x00001000    /* GP Int 1 */
-#define E1000_ICR_GPI_EN2 0x00002000    /* GP Int 2 */
-#define E1000_ICR_GPI_EN3 0x00004000    /* GP Int 3 */
-#define E1000_ICR_TXD_LOW 0x00008000
-#define E1000_ICR_SRPD    0x00010000
+#define E1000_ICR_TXDW          0x00000001 /* Transmit desc written back */
+#define E1000_ICR_TXQE          0x00000002 /* Transmit Queue empty */
+#define E1000_ICR_LSC           0x00000004 /* Link Status Change */
+#define E1000_ICR_RXSEQ         0x00000008 /* rx sequence error */
+#define E1000_ICR_RXDMT0        0x00000010 /* rx desc min. threshold (0) */
+#define E1000_ICR_RXO           0x00000040 /* rx overrun */
+#define E1000_ICR_RXT0          0x00000080 /* rx timer intr (ring 0) */
+#define E1000_ICR_MDAC          0x00000200 /* MDIO access complete */
+#define E1000_ICR_RXCFG         0x00000400 /* RX /c/ ordered set */
+#define E1000_ICR_GPI_EN0       0x00000800 /* GP Int 0 */
+#define E1000_ICR_GPI_EN1       0x00001000 /* GP Int 1 */
+#define E1000_ICR_GPI_EN2       0x00002000 /* GP Int 2 */
+#define E1000_ICR_GPI_EN3       0x00004000 /* GP Int 3 */
+#define E1000_ICR_TXD_LOW       0x00008000
+#define E1000_ICR_SRPD          0x00010000
 
 /* Interrupt Cause Set */
-#define E1000_ICS_TXDW    E1000_ICR_TXDW        /* Transmit desc written back */
-#define E1000_ICS_TXQE    E1000_ICR_TXQE        /* Transmit Queue empty */
-#define E1000_ICS_LSC     E1000_ICR_LSC         /* Link Status Change */
-#define E1000_ICS_RXSEQ   E1000_ICR_RXSEQ       /* rx sequence error */
-#define E1000_ICS_RXDMT0  E1000_ICR_RXDMT0      /* rx desc min. threshold */
-#define E1000_ICS_RXO     E1000_ICR_RXO         /* rx overrun */
-#define E1000_ICS_RXT0    E1000_ICR_RXT0        /* rx timer intr */
-#define E1000_ICS_MDAC    E1000_ICR_MDAC        /* MDIO access complete */
-#define E1000_ICS_RXCFG   E1000_ICR_RXCFG       /* RX /c/ ordered set */
-#define E1000_ICS_GPI_EN0 E1000_ICR_GPI_EN0     /* GP Int 0 */
-#define E1000_ICS_GPI_EN1 E1000_ICR_GPI_EN1     /* GP Int 1 */
-#define E1000_ICS_GPI_EN2 E1000_ICR_GPI_EN2     /* GP Int 2 */
-#define E1000_ICS_GPI_EN3 E1000_ICR_GPI_EN3     /* GP Int 3 */
-#define E1000_ICS_TXD_LOW E1000_ICR_TXD_LOW
-#define E1000_ICS_SRPD    E1000_ICR_SRPD
+#define E1000_ICS_TXDW      E1000_ICR_TXDW      /* Transmit desc written back */
+#define E1000_ICS_TXQE      E1000_ICR_TXQE      /* Transmit Queue empty */
+#define E1000_ICS_LSC       E1000_ICR_LSC       /* Link Status Change */
+#define E1000_ICS_RXSEQ     E1000_ICR_RXSEQ     /* rx sequence error */
+#define E1000_ICS_RXDMT0    E1000_ICR_RXDMT0    /* rx desc min. threshold */
+#define E1000_ICS_RXO       E1000_ICR_RXO       /* rx overrun */
+#define E1000_ICS_RXT0      E1000_ICR_RXT0      /* rx timer intr */
+#define E1000_ICS_MDAC      E1000_ICR_MDAC      /* MDIO access complete */
+#define E1000_ICS_RXCFG     E1000_ICR_RXCFG     /* RX /c/ ordered set */
+#define E1000_ICS_GPI_EN0   E1000_ICR_GPI_EN0   /* GP Int 0 */
+#define E1000_ICS_GPI_EN1   E1000_ICR_GPI_EN1   /* GP Int 1 */
+#define E1000_ICS_GPI_EN2   E1000_ICR_GPI_EN2   /* GP Int 2 */
+#define E1000_ICS_GPI_EN3   E1000_ICR_GPI_EN3   /* GP Int 3 */
+#define E1000_ICS_TXD_LOW   E1000_ICR_TXD_LOW
+#define E1000_ICS_SRPD      E1000_ICR_SRPD
 
 /* Interrupt Mask Set */
-#define E1000_IMS_TXDW    E1000_ICR_TXDW        /* Transmit desc written back */
-#define E1000_IMS_TXQE    E1000_ICR_TXQE        /* Transmit Queue empty */
-#define E1000_IMS_LSC     E1000_ICR_LSC         /* Link Status Change */
-#define E1000_IMS_RXSEQ   E1000_ICR_RXSEQ       /* rx sequence error */
-#define E1000_IMS_RXDMT0  E1000_ICR_RXDMT0      /* rx desc min. threshold */
-#define E1000_IMS_RXO     E1000_ICR_RXO         /* rx overrun */
-#define E1000_IMS_RXT0    E1000_ICR_RXT0        /* rx timer intr */
-#define E1000_IMS_MDAC    E1000_ICR_MDAC        /* MDIO access complete */
-#define E1000_IMS_RXCFG   E1000_ICR_RXCFG       /* RX /c/ ordered set */
-#define E1000_IMS_GPI_EN0 E1000_ICR_GPI_EN0     /* GP Int 0 */
-#define E1000_IMS_GPI_EN1 E1000_ICR_GPI_EN1     /* GP Int 1 */
-#define E1000_IMS_GPI_EN2 E1000_ICR_GPI_EN2     /* GP Int 2 */
-#define E1000_IMS_GPI_EN3 E1000_ICR_GPI_EN3     /* GP Int 3 */
-#define E1000_IMS_TXD_LOW E1000_ICR_TXD_LOW
-#define E1000_IMS_SRPD    E1000_ICR_SRPD
+#define E1000_IMS_TXDW      E1000_ICR_TXDW      /* Transmit desc written back */
+#define E1000_IMS_TXQE      E1000_ICR_TXQE      /* Transmit Queue empty */
+#define E1000_IMS_LSC       E1000_ICR_LSC       /* Link Status Change */
+#define E1000_IMS_RXSEQ     E1000_ICR_RXSEQ     /* rx sequence error */
+#define E1000_IMS_RXDMT0    E1000_ICR_RXDMT0    /* rx desc min. threshold */
+#define E1000_IMS_RXO       E1000_ICR_RXO       /* rx overrun */
+#define E1000_IMS_RXT0      E1000_ICR_RXT0      /* rx timer intr */
+#define E1000_IMS_MDAC      E1000_ICR_MDAC      /* MDIO access complete */
+#define E1000_IMS_RXCFG     E1000_ICR_RXCFG     /* RX /c/ ordered set */
+#define E1000_IMS_GPI_EN0   E1000_ICR_GPI_EN0   /* GP Int 0 */
+#define E1000_IMS_GPI_EN1   E1000_ICR_GPI_EN1   /* GP Int 1 */
+#define E1000_IMS_GPI_EN2   E1000_ICR_GPI_EN2   /* GP Int 2 */
+#define E1000_IMS_GPI_EN3   E1000_ICR_GPI_EN3   /* GP Int 3 */
+#define E1000_IMS_TXD_LOW   E1000_ICR_TXD_LOW
+#define E1000_IMS_SRPD      E1000_ICR_SRPD
 
 /* Interrupt Mask Clear */
-#define E1000_IMC_TXDW    E1000_ICR_TXDW        /* Transmit desc written back */
-#define E1000_IMC_TXQE    E1000_ICR_TXQE        /* Transmit Queue empty */
-#define E1000_IMC_LSC     E1000_ICR_LSC         /* Link Status Change */
-#define E1000_IMC_RXSEQ   E1000_ICR_RXSEQ       /* rx sequence error */
-#define E1000_IMC_RXDMT0  E1000_ICR_RXDMT0      /* rx desc min. threshold */
-#define E1000_IMC_RXO     E1000_ICR_RXO         /* rx overrun */
-#define E1000_IMC_RXT0    E1000_ICR_RXT0        /* rx timer intr */
-#define E1000_IMC_MDAC    E1000_ICR_MDAC        /* MDIO access complete */
-#define E1000_IMC_RXCFG   E1000_ICR_RXCFG       /* RX /c/ ordered set */
-#define E1000_IMC_GPI_EN0 E1000_ICR_GPI_EN0     /* GP Int 0 */
-#define E1000_IMC_GPI_EN1 E1000_ICR_GPI_EN1     /* GP Int 1 */
-#define E1000_IMC_GPI_EN2 E1000_ICR_GPI_EN2     /* GP Int 2 */
-#define E1000_IMC_GPI_EN3 E1000_ICR_GPI_EN3     /* GP Int 3 */
-#define E1000_IMC_TXD_LOW E1000_ICR_TXD_LOW
-#define E1000_IMC_SRPD    E1000_ICR_SRPD
+#define E1000_IMC_TXDW      E1000_ICR_TXDW      /* Transmit desc written back */
+#define E1000_IMC_TXQE      E1000_ICR_TXQE      /* Transmit Queue empty */
+#define E1000_IMC_LSC       E1000_ICR_LSC       /* Link Status Change */
+#define E1000_IMC_RXSEQ     E1000_ICR_RXSEQ     /* rx sequence error */
+#define E1000_IMC_RXDMT0    E1000_ICR_RXDMT0    /* rx desc min. threshold */
+#define E1000_IMC_RXO       E1000_ICR_RXO       /* rx overrun */
+#define E1000_IMC_RXT0      E1000_ICR_RXT0      /* rx timer intr */
+#define E1000_IMC_MDAC      E1000_ICR_MDAC      /* MDIO access complete */
+#define E1000_IMC_RXCFG     E1000_ICR_RXCFG     /* RX /c/ ordered set */
+#define E1000_IMC_GPI_EN0   E1000_ICR_GPI_EN0   /* GP Int 0 */
+#define E1000_IMC_GPI_EN1   E1000_ICR_GPI_EN1   /* GP Int 1 */
+#define E1000_IMC_GPI_EN2   E1000_ICR_GPI_EN2   /* GP Int 2 */
+#define E1000_IMC_GPI_EN3   E1000_ICR_GPI_EN3   /* GP Int 3 */
+#define E1000_IMC_TXD_LOW   E1000_ICR_TXD_LOW
+#define E1000_IMC_SRPD      E1000_ICR_SRPD
 
 /* Receive Control */
-#define E1000_RCTL_RST          0x00000001      /* Software reset */
-#define E1000_RCTL_EN           0x00000002      /* enable */
-#define E1000_RCTL_SBP          0x00000004      /* store bad packet */
-#define E1000_RCTL_UPE          0x00000008      /* unicast promiscuous enable */
-#define E1000_RCTL_MPE          0x00000010      /* multicast promiscuous enab */
-#define E1000_RCTL_LPE          0x00000020      /* long packet enable */
-#define E1000_RCTL_LBM_NO       0x00000000      /* no loopback mode */
-#define E1000_RCTL_LBM_MAC      0x00000040      /* MAC loopback mode */
-#define E1000_RCTL_LBM_SLP      0x00000080      /* serial link loopback mode */
-#define E1000_RCTL_LBM_TCVR     0x000000C0      /* tcvr loopback mode */
-#define E1000_RCTL_RDMTS_HALF   0x00000000      /* rx desc min threshold size */
-#define E1000_RCTL_RDMTS_QUAT   0x00000100      /* rx desc min threshold size */
-#define E1000_RCTL_RDMTS_EIGTH  0x00000200      /* rx desc min threshold size */
-#define E1000_RCTL_MO_SHIFT     12              /* multicast offset shift */
-#define E1000_RCTL_MO_0         0x00000000      /* multicast offset 11:0 */
-#define E1000_RCTL_MO_1         0x00001000      /* multicast offset 12:1 */
-#define E1000_RCTL_MO_2         0x00002000      /* multicast offset 13:2 */
-#define E1000_RCTL_MO_3         0x00003000      /* multicast offset 15:4 */
-#define E1000_RCTL_MDR          0x00004000      /* multicast desc ring 0 */
-#define E1000_RCTL_BAM          0x00008000      /* broadcast enable */
+#define E1000_RCTL_RST            0x00000001    /* Software reset */
+#define E1000_RCTL_EN             0x00000002    /* enable */
+#define E1000_RCTL_SBP            0x00000004    /* store bad packet */
+#define E1000_RCTL_UPE            0x00000008    /* unicast promiscuous enable */
+#define E1000_RCTL_MPE            0x00000010    /* multicast promiscuous enab */
+#define E1000_RCTL_LPE            0x00000020    /* long packet enable */
+#define E1000_RCTL_LBM_NO         0x00000000    /* no loopback mode */
+#define E1000_RCTL_LBM_MAC        0x00000040    /* MAC loopback mode */
+#define E1000_RCTL_LBM_SLP        0x00000080    /* serial link loopback mode */
+#define E1000_RCTL_LBM_TCVR       0x000000C0    /* tcvr loopback mode */
+#define E1000_RCTL_RDMTS_HALF     0x00000000    /* rx desc min threshold size */
+#define E1000_RCTL_RDMTS_QUAT     0x00000100    /* rx desc min threshold size */
+#define E1000_RCTL_RDMTS_EIGTH    0x00000200    /* rx desc min threshold size */
+#define E1000_RCTL_MO_SHIFT       12            /* multicast offset shift */
+#define E1000_RCTL_MO_0           0x00000000    /* multicast offset 11:0 */
+#define E1000_RCTL_MO_1           0x00001000    /* multicast offset 12:1 */
+#define E1000_RCTL_MO_2           0x00002000    /* multicast offset 13:2 */
+#define E1000_RCTL_MO_3           0x00003000    /* multicast offset 15:4 */
+#define E1000_RCTL_MDR            0x00004000    /* multicast desc ring 0 */
+#define E1000_RCTL_BAM            0x00008000    /* broadcast enable */
 /* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */
-#define E1000_RCTL_SZ_2048      0x00000000      /* rx buffer size 2048 */
-#define E1000_RCTL_SZ_1024      0x00010000      /* rx buffer size 1024 */
-#define E1000_RCTL_SZ_512       0x00020000      /* rx buffer size 512 */
-#define E1000_RCTL_SZ_256       0x00030000      /* rx buffer size 256 */
+#define E1000_RCTL_SZ_2048        0x00000000    /* rx buffer size 2048 */
+#define E1000_RCTL_SZ_1024        0x00010000    /* rx buffer size 1024 */
+#define E1000_RCTL_SZ_512         0x00020000    /* rx buffer size 512 */
+#define E1000_RCTL_SZ_256         0x00030000    /* rx buffer size 256 */
 /* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */
-#define E1000_RCTL_SZ_16384     0x00010000      /* rx buffer size 16384 */
-#define E1000_RCTL_SZ_8192      0x00020000      /* rx buffer size 8192 */
-#define E1000_RCTL_SZ_4096      0x00030000      /* rx buffer size 4096 */
-#define E1000_RCTL_VFE          0x00040000      /* vlan filter enable */
-#define E1000_RCTL_CFIEN        0x00080000      /* canonical form enable */
-#define E1000_RCTL_CFI          0x00100000      /* canonical form indicator */
-#define E1000_RCTL_DPF          0x00400000      /* discard pause frames */
-#define E1000_RCTL_PMCF         0x00800000      /* pass MAC control frames */
-#define E1000_RCTL_BSEX         0x02000000      /* Buffer size extension */
+#define E1000_RCTL_SZ_16384       0x00010000    /* rx buffer size 16384 */
+#define E1000_RCTL_SZ_8192        0x00020000    /* rx buffer size 8192 */
+#define E1000_RCTL_SZ_4096        0x00030000    /* rx buffer size 4096 */
+#define E1000_RCTL_VFE            0x00040000    /* vlan filter enable */
+#define E1000_RCTL_CFIEN          0x00080000    /* canonical form enable */
+#define E1000_RCTL_CFI            0x00100000    /* canonical form indicator */
+#define E1000_RCTL_DPF            0x00400000    /* discard pause frames */
+#define E1000_RCTL_PMCF           0x00800000    /* pass MAC control frames */
+#define E1000_RCTL_BSEX           0x02000000    /* Buffer size extension */
 
 /* Receive Descriptor */
 #define E1000_RDT_DELAY 0x0000ffff      /* Delay timer (1=1024us) */
@@ -1426,15 +1475,17 @@
 #define EEPROM_SIZE_128B        0x0000
 #define EEPROM_SIZE_MASK        0x1C00
 
-
 /* EEPROM Word Offsets */
-#define EEPROM_COMPAT              0x0003
-#define EEPROM_ID_LED_SETTINGS     0x0004
-#define EEPROM_INIT_CONTROL1_REG   0x000A
-#define EEPROM_INIT_CONTROL2_REG   0x000F
-#define EEPROM_CFG                 0x0012
-#define EEPROM_FLASH_VERSION       0x0032
-#define EEPROM_CHECKSUM_REG        0x003F
+#define EEPROM_COMPAT                 0x0003
+#define EEPROM_ID_LED_SETTINGS        0x0004
+#define EEPROM_SERDES_AMPLITUDE       0x0006 /* For SERDES output amplitude adjustment. */
+#define EEPROM_INIT_CONTROL1_REG      0x000A
+#define EEPROM_INIT_CONTROL2_REG      0x000F
+#define EEPROM_INIT_CONTROL3_PORT_B   0x0014
+#define EEPROM_INIT_CONTROL3_PORT_A   0x0024
+#define EEPROM_CFG                    0x0012
+#define EEPROM_FLASH_VERSION          0x0032
+#define EEPROM_CHECKSUM_REG           0x003F
 
 /* Word definitions for ID LED Settings */
 #define ID_LED_RESERVED_0000 0x0000
@@ -1458,6 +1509,9 @@
 #define IGP_LED3_MODE           0x07000000
 
 
+/* Mask bits for SERDES amplitude adjustment in Word 6 of the EEPROM */
+#define EEPROM_SERDES_AMPLITUDE_MASK  0x000F
+
 /* Mask bits for fields in Word 0x0a of the EEPROM */
 #define EEPROM_WORD0A_ILOS   0x0010
 #define EEPROM_WORD0A_SWDPIO 0x01E0
@@ -1479,6 +1533,8 @@
 #define EEPROM_NODE_ADDRESS_BYTE_0 0
 #define EEPROM_PBA_BYTE_1          8
 
+#define EEPROM_RESERVED_WORD          0xFFFF
+
 /* EEPROM Map Sizes (Byte Counts) */
 #define PBA_SIZE 4
 
@@ -1490,7 +1546,7 @@
 #define E1000_HDX_COLLISION_DISTANCE    E1000_COLLISION_DISTANCE
 #define E1000_COLD_SHIFT                12
 
-/* The number of Transmit and Receive Descriptors must be a multiple of 8 */
+/* Number of Transmit and Receive Descriptors must be a multiple of 8 */
 #define REQ_TX_DESCRIPTOR_MULTIPLE  8
 #define REQ_RX_DESCRIPTOR_MULTIPLE  8
 
@@ -1557,35 +1613,30 @@
 #define PCIX_STATUS_HI_MMRBC_2K      0x2
 
 
-/* The number of bits that we need to shift right to move the "pause"
- * bits from the EEPROM (bits 13:12) to the "pause" (bits 8:7) field
- * in the TXCW register
+/* Number of bits required to shift right the "pause" bits from the
+ * EEPROM (bits 13:12) to the "pause" (bits 8:7) field in the TXCW register.
  */
 #define PAUSE_SHIFT 5
 
-/* The number of bits that we need to shift left to move the "SWDPIO"
- * bits from the EEPROM (bits 8:5) to the "SWDPIO" (bits 25:22) field
- * in the CTRL register
+/* Number of bits required to shift left the "SWDPIO" bits from the
+ * EEPROM (bits 8:5) to the "SWDPIO" (bits 25:22) field in the CTRL register.
  */
 #define SWDPIO_SHIFT 17
 
-/* The number of bits that we need to shift left to move the "SWDPIO_EXT"
- * bits from the EEPROM word F (bits 7:4) to the bits 11:8 of The
- * Extended CTRL register.
- * in the CTRL register
+/* Number of bits required to shift left the "SWDPIO_EXT" bits from the
+ * EEPROM word F (bits 7:4) to the bits 11:8 of The Extended CTRL register.
  */
 #define SWDPIO__EXT_SHIFT 4
 
-/* The number of bits that we need to shift left to move the "ILOS"
- * bit from the EEPROM (bit 4) to the "ILOS" (bit 7) field
- * in the CTRL register
+/* Number of bits required to shift left the "ILOS" bit from the EEPROM
+ * (bit 4) to the "ILOS" (bit 7) field in the CTRL register.
  */
 #define ILOS_SHIFT  3
 
 
 #define RECEIVE_BUFFER_ALIGN_SIZE  (256)
 
-/* The number of milliseconds we wait for auto-negotiation to complete */
+/* Number of milliseconds we wait for auto-negotiation to complete */
 #define LINK_UP_TIMEOUT             500
 
 #define E1000_TX_BUFFER_SIZE ((uint32_t)1514)
@@ -1668,7 +1719,16 @@
 #define M88E1000_EXT_PHY_SPEC_CTRL 0x14  /* Extended PHY Specific Control */
 #define M88E1000_RX_ERR_CNTR       0x15  /* Receive Error Counter */
 
+#define M88E1000_PHY_EXT_CTRL      0x1A  /* PHY extend control register */
+#define M88E1000_PHY_PAGE_SELECT   0x1D  /* Reg 29 for page number setting */
+#define M88E1000_PHY_GEN_CONTROL   0x1E  /* Its meaning depends on reg 29 */
+#define M88E1000_PHY_VCO_REG_BIT8  0x100 /* Bits 8 & 11 are adjusted for */
+#define M88E1000_PHY_VCO_REG_BIT11 0x800    /* improved BER performance */
+
 #define IGP01E1000_IEEE_REGS_PAGE  0x0000
+#define IGP01E1000_IEEE_RESTART_AUTONEG 0x3300
+#define IGP01E1000_IEEE_FORCE_GIGA      0x0140
+
 /* IGP01E1000 Specific Registers */
 #define IGP01E1000_PHY_PORT_CONFIG 0x10 /* PHY Specific Port Config Register */
 #define IGP01E1000_PHY_PORT_STATUS 0x11 /* PHY Specific Status Register */
@@ -1684,17 +1744,35 @@
 #define IGP01E1000_PHY_AGC_C        0x1472
 #define IGP01E1000_PHY_AGC_D        0x1872
 
-/* Number of AGC registers */
-#define IGP01E1000_PHY_AGC_NUM     4
+/* IGP01E1000 DSP Reset Register */
+#define IGP01E1000_PHY_DSP_RESET   0x1F33
+#define IGP01E1000_PHY_DSP_SET     0x1F71
+#define IGP01E1000_PHY_DSP_FFE     0x1F35
+
+#define IGP01E1000_PHY_CHANNEL_NUM    4
+#define IGP01E1000_PHY_AGC_PARAM_A    0x1171
+#define IGP01E1000_PHY_AGC_PARAM_B    0x1271
+#define IGP01E1000_PHY_AGC_PARAM_C    0x1471
+#define IGP01E1000_PHY_AGC_PARAM_D    0x1871
+
+#define IGP01E1000_PHY_EDAC_MU_INDEX        0xC000
+#define IGP01E1000_PHY_EDAC_SIGN_EXT_9_BITS 0x8000
+
+#define IGP01E1000_PHY_ANALOG_TX_STATE      0x2890
+#define IGP01E1000_PHY_ANALOG_CLASS_A       0x2000
+#define IGP01E1000_PHY_FORCE_ANALOG_ENABLE  0x0004
+#define IGP01E1000_PHY_DSP_FFE_CM_CP        0x0069
 
+#define IGP01E1000_PHY_DSP_FFE_DEFAULT      0x002A
 /* IGP01E1000 PCS Initialization register - stores the polarity status when
  * speed = 1000 Mbps. */
 #define IGP01E1000_PHY_PCS_INIT_REG  0x00B4
+#define IGP01E1000_PHY_PCS_CTRL_REG  0x00B5
 
 #define IGP01E1000_ANALOG_REGS_PAGE  0x20C0
 
 #define MAX_PHY_REG_ADDRESS 0x1F        /* 5 bit address bus (0-0x1F) */
-
+#define MAX_PHY_MULTI_PAGE_REG  0xF     /*Registers that are equal on all pages*/
 /* PHY Control Register */
 #define MII_CR_SPEED_SELECT_MSB 0x0040  /* bits 6,13: 10=1000, 01=100, 00=10 */
 #define MII_CR_COLL_TEST_ENABLE 0x0080  /* Collision test enable */
@@ -1808,8 +1886,11 @@
 #define SR_1000T_LOCAL_RX_STATUS  0x2000 /* Local receiver OK */
 #define SR_1000T_MS_CONFIG_RES    0x4000 /* 1=Local TX is Master, 0=Slave */
 #define SR_1000T_MS_CONFIG_FAULT  0x8000 /* Master/Slave config fault */
-#define SR_1000T_REMOTE_RX_STATUS_SHIFT 12
-#define SR_1000T_LOCAL_RX_STATUS_SHIFT  13
+#define SR_1000T_REMOTE_RX_STATUS_SHIFT          12
+#define SR_1000T_LOCAL_RX_STATUS_SHIFT           13
+#define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT    5
+#define FFE_IDLE_ERR_COUNT_TIMEOUT_20            20
+#define FFE_IDLE_ERR_COUNT_TIMEOUT_100           100
 
 /* Extended Status Register */
 #define IEEE_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */
@@ -1901,7 +1982,6 @@
 #define M88E1000_EPSCR_TX_CLK_25      0x0070 /* 25  MHz TX_CLK */
 #define M88E1000_EPSCR_TX_CLK_0       0x0000 /* NO  TX_CLK */
 
-
 /* IGP01E1000 Specific Port Config Register - R/W */
 #define IGP01E1000_PSCFR_AUTO_MDIX_PAR_DETECT  0x0010
 #define IGP01E1000_PSCFR_PRE_EN                0x0020
@@ -1952,6 +2032,11 @@
 #define IGP01E1000_MSE_CHANNEL_B        0x0F00
 #define IGP01E1000_MSE_CHANNEL_A        0xF000
 
+/* IGP01E1000 DSP reset macros */
+#define DSP_RESET_ENABLE     0x0
+#define DSP_RESET_DISABLE    0x2
+#define E1000_MAX_DSP_RESETS 10
+
 /* IGP01E1000 AGC Registers */
 
 #define IGP01E1000_AGC_LENGTH_SHIFT 7         /* Coarse - 13:11, Fine - 10:7 */
@@ -1962,18 +2047,6 @@
 /* The precision of the length is +/- 10 meters */
 #define IGP01E1000_AGC_RANGE    10
 
-/* IGP cable length table */
-static const
-uint16_t e1000_igp_cable_length_table[IGP01E1000_AGC_LENGTH_TABLE_SIZE] =
-    { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
-      5, 10, 10, 10, 10, 10, 10, 10, 20, 20, 20, 20, 20, 25, 25, 25,
-      25, 25, 25, 25, 30, 30, 30, 30, 40, 40, 40, 40, 40, 40, 40, 40,
-      40, 50, 50, 50, 50, 50, 50, 50, 60, 60, 60, 60, 60, 60, 60, 60,
-      60, 70, 70, 70, 70, 70, 70, 80, 80, 80, 80, 80, 80, 90, 90, 90,
-      90, 90, 90, 90, 90, 90, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
-      100, 100, 100, 100, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110,
-      110, 110, 110, 110, 110, 110, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120};
-
 /* IGP01E1000 PCS Initialization register */
 /* bits 3:6 in the PCS registers stores the channels polarity */
 #define IGP01E1000_PHY_POLARITY_MASK    0x0078
@@ -1982,11 +2055,12 @@
 #define IGP01E1000_GMII_FLEX_SPD               0x10 /* Enable flexible speed
                                                      * on Link-Up */
 #define IGP01E1000_GMII_SPD                    0x20 /* Enable SPD */
+
 /* IGP01E1000 Analog Register */
-#define IGP01E1000_ANALOG_SPARE_FUSE_STATUS         0x0011
-#define IGP01E1000_ANALOG_FUSE_STATUS               0x0010
-#define IGP01E1000_ANALOG_FUSE_CONTROL              0x001C
-#define IGP01E1000_ANALOG_FUSE_BYPASS               0x001E
+#define IGP01E1000_ANALOG_SPARE_FUSE_STATUS       0x20D1
+#define IGP01E1000_ANALOG_FUSE_STATUS             0x20D0
+#define IGP01E1000_ANALOG_FUSE_CONTROL            0x20DC
+#define IGP01E1000_ANALOG_FUSE_BYPASS             0x20DE
 
 #define IGP01E1000_ANALOG_FUSE_POLY_MASK            0xF000
 #define IGP01E1000_ANALOG_FUSE_FINE_MASK            0x0F80
@@ -2032,5 +2106,8 @@
 #define ADVERTISE_1000_HALF 0x0010
 #define ADVERTISE_1000_FULL 0x0020
 #define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x002F  /* Everything but 1000-Half */
+#define AUTONEG_ADVERTISE_10_100_ALL    0x000F /* All 10/100 speeds*/
+#define AUTONEG_ADVERTISE_10_ALL        0x0003 /* 10Mbps Full & Half speeds*/
+
 
 #endif /* _E1000_HW_H_ */
diff -urN linux-2.4.23-pre5/drivers/net/e1000/e1000_main.c linux-2.4.23-pre6/drivers/net/e1000/e1000_main.c
--- linux-2.4.23-pre5/drivers/net/e1000/e1000_main.c	2003-08-25 04:44:42.000000000 -0700
+++ linux-2.4.23-pre6/drivers/net/e1000/e1000_main.c	2003-10-01 10:38:50.000000000 -0700
@@ -30,6 +30,22 @@
 
 /* Change Log
  *
+ * 5.2.16	8/8/03
+ *   o Added support for new controllers: 82545GM, 82546GB, 82541/7_B1
+ *   o Bug fix: reset h/w before first EEPROM read because we don't know
+ *     who may have been messing with the device before we got there.
+ *     [Dave Johnson (ddj -a-t- cascv.brown.edu)]
+ *   o Bug fix: read the correct work from EEPROM to detect programmed
+ *     WoL settings.
+ *   o Bug fix: TSO would hang if space left in FIFO was being miscalculated
+ *     when mss dropped without a correspoding drop in the DMA buffer size.
+ *   o ASF for Fiber nics isn't supported.
+ *   o Bug fix: Workaround added for potential hang with 82544 running in
+ *     PCI-X if send buffer terminates within an evenly-aligned dword.
+ *   o Feature: Add support for ethtool flow control setting.
+ *   o Feature: Add support for ethtool TSO setting.
+ *   o Feature: Increase default Tx Descriptor count to 1024 for >= 82544.
+ *   
  * 5.1.13	5/28/03
  *   o Bug fix: request_irq() failure resulted in freeing resources twice!
  *     [Don Fry (brazilnut@us.ibm.com)]
@@ -39,18 +55,11 @@
  *   o Cleanup: s/int/unsigned int/ for descriptor ring indexes.
  *   
  * 5.1.11	5/6/03
- *   o Feature: Added support for 82546EB (Quad-port) hardware.
- *   o Feature: Added support for Diagnostics through Ethtool.
- *   o Cleanup: Removed /proc support.
- *   o Cleanup: Removed proprietary IDIAG interface.
- *   o Bug fix: TSO bug fixes.
- *
- * 5.0.42	3/5/03
  */
 
 char e1000_driver_name[] = "e1000";
 char e1000_driver_string[] = "Intel(R) PRO/1000 Network Driver";
-char e1000_driver_version[] = "5.1.13-k1";
+char e1000_driver_version[] = "5.2.16-k1";
 char e1000_copyright[] = "Copyright (c) 1999-2003 Intel Corporation.";
 
 /* e1000_pci_tbl - PCI Device ID Table
@@ -61,7 +70,7 @@
  * { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
  *   Class, Class Mask, private data (not used) }
  */
-static struct pci_device_id e1000_pci_tbl[] __devinitdata = {
+static struct pci_device_id e1000_pci_tbl[] = {
 	{0x8086, 0x1000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
 	{0x8086, 0x1001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
 	{0x8086, 0x1004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
@@ -71,15 +80,28 @@
 	{0x8086, 0x100D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
 	{0x8086, 0x100E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
 	{0x8086, 0x100F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
-	{0x8086, 0x1011, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
 	{0x8086, 0x1010, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x8086, 0x1011, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
 	{0x8086, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x8086, 0x1013, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x8086, 0x1014, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x8086, 0x1015, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
 	{0x8086, 0x1016, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
 	{0x8086, 0x1017, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
-	{0x8086, 0x101E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
-	{0x8086, 0x101D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
-	{0x8086, 0x1013, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x8086, 0x1018, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
 	{0x8086, 0x1019, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x8086, 0x101D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x8086, 0x101E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x8086, 0x1026, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x8086, 0x1027, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x8086, 0x1028, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x8086, 0x1075, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x8086, 0x1076, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x8086, 0x1077, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x8086, 0x1078, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x8086, 0x1079, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x8086, 0x107A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x8086, 0x107B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
 	/* required last entry */
 	{0,}
 };
@@ -120,7 +142,7 @@
 static void e1000_update_stats(struct e1000_adapter *adapter);
 static inline void e1000_irq_disable(struct e1000_adapter *adapter);
 static inline void e1000_irq_enable(struct e1000_adapter *adapter);
-static void e1000_intr(int irq, void *data, struct pt_regs *regs);
+static irqreturn_t e1000_intr(int irq, void *data, struct pt_regs *regs);
 static boolean_t e1000_clean_tx_irq(struct e1000_adapter *adapter);
 #ifdef CONFIG_E1000_NAPI
 static int e1000_clean(struct net_device *netdev, int *budget);
@@ -425,6 +447,11 @@
 	if(pci_using_dac)
 		netdev->features |= NETIF_F_HIGHDMA;
 
+	/* before reading the EEPROM, reset the controller to 
+	 * put the device in a known good starting state */
+	
+	e1000_reset_hw(&adapter->hw);
+
 	/* make sure the EEPROM is good */
 
 	if(e1000_validate_eeprom_checksum(&adapter->hw) < 0) {
@@ -475,12 +502,28 @@
 	 * enable the ACPI Magic Packet filter
 	 */
 
-	e1000_read_eeprom(&adapter->hw, EEPROM_INIT_CONTROL2_REG,1, &eeprom_data);
-	if((adapter->hw.mac_type >= e1000_82544) &&
-	   (eeprom_data & E1000_EEPROM_APME))
+	switch(adapter->hw.mac_type) {
+	case e1000_82542_rev2_0:
+	case e1000_82542_rev2_1:
+	case e1000_82543:
+		break;
+	case e1000_82546:
+	case e1000_82546_rev_3:
+		if((E1000_READ_REG(&adapter->hw, STATUS) & E1000_STATUS_FUNC_1)
+		   && (adapter->hw.media_type == e1000_media_type_copper)) {
+			e1000_read_eeprom(&adapter->hw,
+				EEPROM_INIT_CONTROL3_PORT_B, 1, &eeprom_data);
+			break;
+		}
+		/* Fall Through */
+	default:
+		e1000_read_eeprom(&adapter->hw,
+			EEPROM_INIT_CONTROL3_PORT_A, 1, &eeprom_data);
+		break;
+	}
+	if(eeprom_data & E1000_EEPROM_APME)
 		adapter->wol |= E1000_WUFC_MAG;
 
-
 	/* reset the hardware with the new settings */
 
 	e1000_reset(adapter);
@@ -515,7 +558,8 @@
 	struct e1000_adapter *adapter = netdev->priv;
 	uint32_t manc;
 
-	if(adapter->hw.mac_type >= e1000_82540) {
+	if(adapter->hw.mac_type >= e1000_82540 &&
+	   adapter->hw.media_type == e1000_media_type_copper) {
 		manc = E1000_READ_REG(&adapter->hw, MANC);
 		if(manc & E1000_MANC_SMBUS_EN) {
 			manc |= E1000_MANC_ARP_EN;
@@ -583,21 +627,13 @@
 	hw->fc_pause_time = E1000_FC_PAUSE_TIME;
 	hw->fc_send_xon = 1;
 
-	if((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547))
+	if((hw->mac_type == e1000_82541) ||
+	   (hw->mac_type == e1000_82547) ||
+	   (hw->mac_type == e1000_82541_rev_2) ||
+	   (hw->mac_type == e1000_82547_rev_2))
 		hw->phy_init_script = 1;
 
-	/* Media type - copper or fiber */
-
-	if(hw->mac_type >= e1000_82543) {
-		uint32_t status = E1000_READ_REG(hw, STATUS);
-
-		if(status & E1000_STATUS_TBIMODE)
-			hw->media_type = e1000_media_type_fiber;
-		else
-			hw->media_type = e1000_media_type_copper;
-	} else {
-		hw->media_type = e1000_media_type_fiber;
-	}
+	e1000_set_media_type(hw);
 
 	if(hw->mac_type < e1000_82543)
 		hw->report_tx_early = 0;
@@ -613,6 +649,7 @@
 	if(hw->media_type == e1000_media_type_copper) {
 		hw->mdix = AUTO_ALL_MODES;
 		hw->disable_polarity_correction = FALSE;
+		hw->master_slave = E1000_MASTER_SLAVE;
 	}
 
 	atomic_set(&adapter->irq_sem, 1);
@@ -762,7 +799,8 @@
 		tipg |= DEFAULT_82542_TIPG_IPGR2 << E1000_TIPG_IPGR2_SHIFT;
 		break;
 	default:
-		if(adapter->hw.media_type == e1000_media_type_fiber)
+		if(adapter->hw.media_type == e1000_media_type_fiber ||
+		   adapter->hw.media_type == e1000_media_type_internal_serdes)
 			tipg = DEFAULT_82543_TIPG_IPGT_FIBER;
 		else
 			tipg = DEFAULT_82543_TIPG_IPGT_COPPER;
@@ -797,6 +835,12 @@
 		adapter->txd_cmd |= E1000_TXD_CMD_RS;
 	else
 		adapter->txd_cmd |= E1000_TXD_CMD_RPS;
+
+	/* Cache if we're 82544 running in PCI-X because we'll
+	 * need this to apply a workaround later in the send path. */
+	if(adapter->hw.mac_type == e1000_82544 &&
+	   adapter->hw.bus_type == e1000_bus_type_pcix)
+		adapter->pcix_82544 = 1;
 }
 
 /**
@@ -1489,11 +1533,18 @@
 {
 	struct e1000_desc_ring *tx_ring = &adapter->tx_ring;
 	struct e1000_buffer *buffer_info;
-	int len = skb->len;
+	unsigned int len = skb->len, max_per_txd = E1000_MAX_DATA_PER_TXD;
 	unsigned int offset = 0, size, count = 0, i;
 
 #ifdef NETIF_F_TSO
-	unsigned int tso = skb_shinfo(skb)->tso_size;
+	unsigned int mss = skb_shinfo(skb)->tso_size;
+	/* The controller does a simple calculation to 
+	 * make sure there is enough room in the FIFO before
+	 * initiating the DMA for each buffer.  The calc is:
+	 * 4 = ceil(buffer len/mss).  To make sure we don't
+	 * overrun the FIFO, adjust the max buffer len if mss
+	 * drops. */
+	if(mss) max_per_txd = min(mss << 2, max_per_txd);
 #endif
 	unsigned int nr_frags = skb_shinfo(skb)->nr_frags;
 	unsigned int f;
@@ -1503,13 +1554,20 @@
 
 	while(len) {
 		buffer_info = &tx_ring->buffer_info[i];
-		size = min(len, E1000_MAX_DATA_PER_TXD);
+		size = min(len, max_per_txd);
 #ifdef NETIF_F_TSO
 		/* Workaround for premature desc write-backs
 		 * in TSO mode.  Append 4-byte sentinel desc */
-		if(tso && !nr_frags && size == len && size > 4)
+		if(mss && !nr_frags && size == len && size > 8)
 			size -= 4;
 #endif
+		/* Workaround for potential 82544 hang in PCI-X.  Avoid
+		 * terminating buffers within evenly-aligned dwords. */
+		if(adapter->pcix_82544 &&
+		   !((unsigned long)(skb->data + offset + size - 1) & 4) &&
+		   size > 4)
+			size -= 4;
+
 		buffer_info->length = size;
 		buffer_info->dma =
 			pci_map_single(adapter->pdev,
@@ -1529,22 +1587,30 @@
 
 		frag = &skb_shinfo(skb)->frags[f];
 		len = frag->size;
-		offset = 0;
+		offset = frag->page_offset;
 
 		while(len) {
 			buffer_info = &tx_ring->buffer_info[i];
-			size = min(len, E1000_MAX_DATA_PER_TXD);
+			size = min(len, max_per_txd);
 #ifdef NETIF_F_TSO
 			/* Workaround for premature desc write-backs
 			 * in TSO mode.  Append 4-byte sentinel desc */
-			if(tso && f == (nr_frags-1) && size == len && size > 4)
+			if(mss && f == (nr_frags-1) && size == len && size > 8)
 				size -= 4;
 #endif
+			/* Workaround for potential 82544 hang in PCI-X.
+			 * Avoid terminating buffers within evenly-aligned
+			 * dwords. */
+			if(adapter->pcix_82544 &&
+			   !((unsigned long)(frag->page+offset+size-1) & 4) &&
+			   size > 4)
+				size -= 4;
+
 			buffer_info->length = size;
 			buffer_info->dma =
 				pci_map_page(adapter->pdev,
 					frag->page,
-					frag->page_offset + offset,
+					offset,
 					size,
 					PCI_DMA_TODEVICE);
 			buffer_info->time_stamp = jiffies;
@@ -1555,6 +1621,31 @@
 			if(++i == tx_ring->count) i = 0;
 		}
 	}
+
+	if(E1000_DESC_UNUSED(&adapter->tx_ring) < count) {
+
+		/* There aren't enough descriptors available to queue up
+		 * this send, so undo the mapping and abort the send. 
+		 * We could have done the check before we mapped the skb,
+		 * but because of all the workarounds (above), it's too
+		 * difficult to predict how many we're going to need.*/
+		i = first;
+
+		while(count--) {
+			buffer_info = &tx_ring->buffer_info[i];
+			if(buffer_info->dma) {
+				pci_unmap_page(adapter->pdev,
+					       buffer_info->dma,
+					       buffer_info->length,
+					       PCI_DMA_TODEVICE);
+				buffer_info->dma = 0;
+			}
+			if(++i == tx_ring->count) i = 0;
+		}
+
+		return 0;
+	}
+
 	i = (i == 0) ? tx_ring->count - 1 : i - 1;
 	tx_ring->buffer_info[i].skb = skb;
 	tx_ring->buffer_info[first].next_to_watch = i;
@@ -1649,29 +1740,19 @@
 	return 0;
 }
 
-/* Tx Descriptors needed, worst case */
-#define TXD_USE_COUNT(S) (((S) >> E1000_MAX_TXD_PWR) + \
-			 (((S) & (E1000_MAX_DATA_PER_TXD - 1)) ? 1 : 0))
-#define DESC_NEEDED TXD_USE_COUNT(MAX_JUMBO_FRAME_SIZE) + \
-	MAX_SKB_FRAGS * TXD_USE_COUNT(PAGE_SIZE) + 1
-
 static int
 e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
 {
 	struct e1000_adapter *adapter = netdev->priv;
 	unsigned int first;
 	unsigned int tx_flags = 0;
+	int count;
 
 	if(skb->len <= 0) {
 		dev_kfree_skb_any(skb);
 		return 0;
 	}
 
-	if(E1000_DESC_UNUSED(&adapter->tx_ring) < DESC_NEEDED) {
-		netif_stop_queue(netdev);
-		return 1;
-	}
-
 	if(adapter->hw.mac_type == e1000_82547) {
 		if(e1000_82547_fifo_workaround(adapter, skb)) {
 			netif_stop_queue(netdev);
@@ -1692,7 +1773,12 @@
 	else if(e1000_tx_csum(adapter, skb))
 		tx_flags |= E1000_TX_FLAGS_CSUM;
 
-	e1000_tx_queue(adapter, e1000_tx_map(adapter, skb, first), tx_flags);
+	if((count = e1000_tx_map(adapter, skb, first)))
+		e1000_tx_queue(adapter, count, tx_flags);
+	else {
+		netif_stop_queue(netdev);
+		return 1;
+	}
 
 	netdev->trans_start = jiffies;
 
@@ -1965,7 +2051,7 @@
  * @pt_regs: CPU registers structure
  **/
 
-static void
+static irqreturn_t
 e1000_intr(int irq, void *data, struct pt_regs *regs)
 {
 	struct net_device *netdev = data;
@@ -1976,7 +2062,7 @@
 #endif
 
 	if(!icr)
-		return;  /* Not our interrupt */
+		return IRQ_NONE;  /* Not our interrupt */
 
 	if(icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) {
 		adapter->hw.get_link_status = 1;
@@ -2000,6 +2086,8 @@
 		   !e1000_clean_tx_irq(adapter))
 			break;
 #endif
+
+	return IRQ_HANDLED;
 }
 
 #ifdef CONFIG_E1000_NAPI
@@ -2385,7 +2473,7 @@
 	uint16_t mii_reg;
 	uint16_t spddplx;
 
-	if(adapter->hw.media_type == e1000_media_type_fiber)
+	if(adapter->hw.media_type != e1000_media_type_copper)
 		return -EOPNOTSUPP;
 
 	switch (cmd) {
@@ -2657,7 +2745,7 @@
 	case SYS_DOWN:
 	case SYS_HALT:
 	case SYS_POWER_OFF:
-		pci_for_each_dev(pdev) {
+		while((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev))) {
 			if(pci_dev_driver(pdev) == &e1000_driver)
 				e1000_suspend(pdev, 3);
 		}
@@ -2704,7 +2792,8 @@
 			E1000_WRITE_REG(&adapter->hw, CTRL, ctrl);
 		}
 
-		if(adapter->hw.media_type == e1000_media_type_fiber) {
+		if(adapter->hw.media_type == e1000_media_type_fiber ||
+		   adapter->hw.media_type == e1000_media_type_internal_serdes) {
 			/* keep the laser running in D3 */
 			ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT);
 			ctrl_ext |= E1000_CTRL_EXT_SDP7_DATA;
@@ -2724,7 +2813,8 @@
 
 	pci_save_state(pdev, adapter->pci_state);
 
-	if(adapter->hw.mac_type >= e1000_82540) {
+	if(adapter->hw.mac_type >= e1000_82540 &&
+	   adapter->hw.media_type == e1000_media_type_copper) {
 		manc = E1000_READ_REG(&adapter->hw, MANC);
 		if(manc & E1000_MANC_SMBUS_EN) {
 			manc |= E1000_MANC_ARP_EN;
@@ -2762,7 +2852,8 @@
 
 	netif_device_attach(netdev);
 
-	if(adapter->hw.mac_type >= e1000_82540) {
+	if(adapter->hw.mac_type >= e1000_82540 &&
+	   adapter->hw.media_type == e1000_media_type_copper) {
 		manc = E1000_READ_REG(&adapter->hw, MANC);
 		manc &= ~(E1000_MANC_ARP_EN);
 		E1000_WRITE_REG(&adapter->hw, MANC, manc);
diff -urN linux-2.4.23-pre5/drivers/net/e1000/e1000_osdep.h linux-2.4.23-pre6/drivers/net/e1000/e1000_osdep.h
--- linux-2.4.23-pre5/drivers/net/e1000/e1000_osdep.h	2003-08-25 04:44:42.000000000 -0700
+++ linux-2.4.23-pre6/drivers/net/e1000/e1000_osdep.h	2003-10-01 10:38:50.000000000 -0700
@@ -55,10 +55,13 @@
 #define CMD_MEM_WRT_INVALIDATE PCI_COMMAND_INVALIDATE
 
 typedef enum {
+#undef FALSE
     FALSE = 0,
+#undef TRUE
     TRUE = 1
 } boolean_t;
 
+#undef ASSERT
 #define ASSERT(x)	if(!(x)) BUG()
 #define MSGOUT(S, A, B)	printk(KERN_DEBUG S "\n", A, B)
 
diff -urN linux-2.4.23-pre5/drivers/net/e1000/e1000_param.c linux-2.4.23-pre6/drivers/net/e1000/e1000_param.c
--- linux-2.4.23-pre5/drivers/net/e1000/e1000_param.c	2003-06-13 07:51:34.000000000 -0700
+++ linux-2.4.23-pre6/drivers/net/e1000/e1000_param.c	2003-10-01 10:38:50.000000000 -0700
@@ -140,7 +140,7 @@
  * Valid Range: 0, 1
  *  - 0 - disables all checksum offload
  *  - 1 - enables receive IP/TCP/UDP checksum offload
- *        on 82543 based NICs
+ *        on 82543 and newer -based NICs
  *
  * Default Value: 1
  */
@@ -458,6 +458,7 @@
 
 	switch(adapter->hw.media_type) {
 	case e1000_media_type_fiber:
+	case e1000_media_type_internal_serdes:
 		e1000_check_fiber_options(adapter);
 		break;
 	case e1000_media_type_copper:
@@ -601,7 +602,7 @@
 
 	switch (speed + dplx) {
 	case 0:
-		adapter->hw.autoneg = 1;
+		adapter->hw.autoneg = adapter->fc_autoneg = 1;
 		if(Speed[bd] != OPTION_UNSET || Duplex[bd] != OPTION_UNSET)
 			printk(KERN_INFO
 			       "Speed and duplex autonegotiation enabled\n");
@@ -609,14 +610,14 @@
 	case HALF_DUPLEX:
 		printk(KERN_INFO "Half Duplex specified without Speed\n");
 		printk(KERN_INFO "Using Autonegotiation at Half Duplex only\n");
-		adapter->hw.autoneg = 1;
+		adapter->hw.autoneg = adapter->fc_autoneg = 1;
 		adapter->hw.autoneg_advertised = ADVERTISE_10_HALF |
 		                                 ADVERTISE_100_HALF;
 		break;
 	case FULL_DUPLEX:
 		printk(KERN_INFO "Full Duplex specified without Speed\n");
 		printk(KERN_INFO "Using Autonegotiation at Full Duplex only\n");
-		adapter->hw.autoneg = 1;
+		adapter->hw.autoneg = adapter->fc_autoneg = 1;
 		adapter->hw.autoneg_advertised = ADVERTISE_10_FULL |
 		                                 ADVERTISE_100_FULL |
 		                                 ADVERTISE_1000_FULL;
@@ -624,38 +625,38 @@
 	case SPEED_10:
 		printk(KERN_INFO "10 Mbps Speed specified without Duplex\n");
 		printk(KERN_INFO "Using Autonegotiation at 10 Mbps only\n");
-		adapter->hw.autoneg = 1;
+		adapter->hw.autoneg = adapter->fc_autoneg = 1;
 		adapter->hw.autoneg_advertised = ADVERTISE_10_HALF |
 		                                 ADVERTISE_10_FULL;
 		break;
 	case SPEED_10 + HALF_DUPLEX:
 		printk(KERN_INFO "Forcing to 10 Mbps Half Duplex\n");
-		adapter->hw.autoneg = 0;
+		adapter->hw.autoneg = adapter->fc_autoneg = 0;
 		adapter->hw.forced_speed_duplex = e1000_10_half;
 		adapter->hw.autoneg_advertised = 0;
 		break;
 	case SPEED_10 + FULL_DUPLEX:
 		printk(KERN_INFO "Forcing to 10 Mbps Full Duplex\n");
-		adapter->hw.autoneg = 0;
+		adapter->hw.autoneg = adapter->fc_autoneg = 0;
 		adapter->hw.forced_speed_duplex = e1000_10_full;
 		adapter->hw.autoneg_advertised = 0;
 		break;
 	case SPEED_100:
 		printk(KERN_INFO "100 Mbps Speed specified without Duplex\n");
 		printk(KERN_INFO "Using Autonegotiation at 100 Mbps only\n");
-		adapter->hw.autoneg = 1;
+		adapter->hw.autoneg = adapter->fc_autoneg = 1;
 		adapter->hw.autoneg_advertised = ADVERTISE_100_HALF |
 		                                 ADVERTISE_100_FULL;
 		break;
 	case SPEED_100 + HALF_DUPLEX:
 		printk(KERN_INFO "Forcing to 100 Mbps Half Duplex\n");
-		adapter->hw.autoneg = 0;
+		adapter->hw.autoneg = adapter->fc_autoneg = 0;
 		adapter->hw.forced_speed_duplex = e1000_100_half;
 		adapter->hw.autoneg_advertised = 0;
 		break;
 	case SPEED_100 + FULL_DUPLEX:
 		printk(KERN_INFO "Forcing to 100 Mbps Full Duplex\n");
-		adapter->hw.autoneg = 0;
+		adapter->hw.autoneg = adapter->fc_autoneg = 0;
 		adapter->hw.forced_speed_duplex = e1000_100_full;
 		adapter->hw.autoneg_advertised = 0;
 		break;
@@ -663,20 +664,20 @@
 		printk(KERN_INFO "1000 Mbps Speed specified without Duplex\n");
 		printk(KERN_INFO
 		       "Using Autonegotiation at 1000 Mbps Full Duplex only\n");
-		adapter->hw.autoneg = 1;
+		adapter->hw.autoneg = adapter->fc_autoneg = 1;
 		adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL;
 		break;
 	case SPEED_1000 + HALF_DUPLEX:
 		printk(KERN_INFO "Half Duplex is not supported at 1000 Mbps\n");
 		printk(KERN_INFO
 		       "Using Autonegotiation at 1000 Mbps Full Duplex only\n");
-		adapter->hw.autoneg = 1;
+		adapter->hw.autoneg = adapter->fc_autoneg = 1;
 		adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL;
 		break;
 	case SPEED_1000 + FULL_DUPLEX:
 		printk(KERN_INFO
 		       "Using Autonegotiation at 1000 Mbps Full Duplex only\n");
-		adapter->hw.autoneg = 1;
+		adapter->hw.autoneg = adapter->fc_autoneg = 1;
 		adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL;
 		break;
 	default:
diff -urN linux-2.4.23-pre5/drivers/net/ibm_emac/ibm_emac.h linux-2.4.23-pre6/drivers/net/ibm_emac/ibm_emac.h
--- linux-2.4.23-pre5/drivers/net/ibm_emac/ibm_emac.h	2003-08-25 04:44:42.000000000 -0700
+++ linux-2.4.23-pre6/drivers/net/ibm_emac/ibm_emac.h	2003-10-01 10:38:50.000000000 -0700
@@ -85,22 +85,52 @@
 #define EMAC_M1_IST			0x01000000
 #define EMAC_M1_MF_1000MBPS		0x00800000	/* 0's for 10MBPS */
 #define EMAC_M1_MF_100MBPS		0x00400000
+#define EMAC_M1_TR			0x00008000
+#ifdef CONFIG_IBM_EMAC4
+#define EMAC_M1_RFS_16K                 0x00280000	/* 000 for 512 byte */
+#define EMAC_M1_RFS_8K                  0x00200000
+#define EMAC_M1_RFS_4K                  0x00180000
+#define EMAC_M1_RFS_2K                  0x00100000
+#define EMAC_M1_RFS_1K                  0x00080000
+#define EMAC_M1_TX_FIFO_16K             0x00050000	/* 0's for 512 byte */
+#define EMAC_M1_TX_FIFO_8K              0x00040000
+#define EMAC_M1_TX_FIFO_4K              0x00030000
+#define EMAC_M1_TX_FIFO_2K              0x00020000
+#define EMAC_M1_TX_FIFO_1K              0x00010000
+#define EMAC_M1_TX_MWSW                 0x00001000	/* 0 wait for status */
+#define EMAC_M1_JUMBO_ENABLE            0x00000800	/* Upt to 9Kr status */
+#define EMAC_M1_OPB_CLK_66              0x00000010	/* 66Mhz */
+#define EMAC_M1_OPB_CLK_83              0x00000008	/* 83hz */
+#define EMAC_M1_OPB_CLK_100             0x00000018	/* 100Mhz */
+#define EMAC_M1_OPB_CLK_100P            0x00000020	/* 100Mhz+ */
+#else /* CONFIG_IBM_EMAC4 */
 #define EMAC_M1_RFS_4K			0x00300000	/* ~4k for 512 byte */
 #define EMAC_M1_RFS_2K			0x00200000
 #define EMAC_M1_RFS_1K			0x00100000
 #define EMAC_M1_TX_FIFO_2K		0x00080000	/* 0's for 512 byte */
 #define EMAC_M1_TX_FIFO_1K		0x00040000
 #define EMAC_M1_TR0_DEPEND		0x00010000	/* 0'x for single packet */
-#define EMAC_M1_TR0_MULTI		0x00008000
 #define EMAC_M1_TR1_DEPEND		0x00004000
 #define EMAC_M1_TR1_MULTI		0x00002000
 #define EMAC_M1_JUMBO_ENABLE		0x00001000
+#endif /* CONFIG_IBM_EMAC4 */
+#define EMAC_M1_BASE			(EMAC_M1_RFS_4K | \
+					EMAC_M1_TX_FIFO_2K | \
+					EMAC_M1_APP | \
+					EMAC_M1_TR)
 
 /* Transmit Mode Register 0 */
-#define EMAC_TXM0_GNP0			0x80000000
-#define EMAC_TXM0_GNP1			0x40000000
-#define EMAC_TXM0_GNPD			0x20000000
-#define EMAC_TXM0_FC			0x10000000
+#define EMAC_TMR0_GNP0			0x80000000
+#define EMAC_TMR0_GNP1			0x40000000
+#define EMAC_TMR0_GNPD			0x20000000
+#define EMAC_TMR0_FC			0x10000000
+#define EMAC_TMR0_TFAE_2_32		0x00000001
+#define EMAC_TMR0_TFAE_4_64		0x00000002
+#define EMAC_TMR0_TFAE_8_128		0x00000003
+#define EMAC_TMR0_TFAE_16_256		0x00000004
+#define EMAC_TMR0_TFAE_32_512		0x00000005
+#define EMAC_TMR0_TFAE_64_1024		0x00000006
+#define EMAC_TMR0_TFAE_128_2048		0x00000007
 
 /* Receive Mode Register */
 #define EMAC_RMR_SP			0x80000000
@@ -116,6 +146,14 @@
 #define EMAC_RMR_MIAE			0x00200000
 #define EMAC_RMR_BAE			0x00100000
 #define EMAC_RMR_MAE			0x00080000
+#define EMAC_RMR_RFAF_2_32		0x00000001
+#define EMAC_RMR_RFAF_4_64		0x00000002
+#define EMAC_RMR_RFAF_8_128		0x00000003
+#define EMAC_RMR_RFAF_16_256		0x00000004
+#define EMAC_RMR_RFAF_32_512		0x00000005
+#define EMAC_RMR_RFAF_64_1024		0x00000006
+#define EMAC_RMR_RFAF_128_2048		0x00000007
+#define EMAC_RMR_BASE			(EMAC_RMR_IAE | EMAC_RMR_BAE)
 
 /* Interrupt Status & enable Regs */
 #define EMAC_ISR_OVR			0x02000000
@@ -148,11 +186,11 @@
 #define EMAC_STACR_CLK_100MHZ		0x00000C00
 
 /* Transmit Request Threshold Register */
-#define EMAC_TRTR_256			0x18000000	/* 0's for 64 Bytes */
+#define EMAC_TRTR_1600			0x18000000	/* 0's for 64 Bytes */
+#define EMAC_TRTR_256			0x03000000
 #define EMAC_TRTR_192			0x10000000
 #define EMAC_TRTR_128			0x01000000
 
-
 #define EMAC_TX_CTRL_GFCS		0x0200
 #define EMAC_TX_CTRL_GP			0x0100
 #define EMAC_TX_CTRL_ISA		0x0080
@@ -176,8 +214,6 @@
 #define EMAC_TX_ST_UR			0x0002
 #define EMAC_TX_ST_SQE			0x0001
 
-#define EMAC_TX_ST_DEFAULT		0x03F3
-
 /* madmal receive status / Control bits */
 #define EMAC_RX_ST_OE			0x0200
 #define EMAC_RX_ST_PP			0x0100
@@ -191,5 +227,36 @@
 #define EMAC_RX_ST_IRE			0x0001
 #define EMAC_BAD_RX_PACKET		0x02ff
 
+/* Revision specific EMAC register defaults */
+#ifdef CONFIG_IBM_EMAC4
+#define EMAC_M1_DEFAULT			(EMAC_M1_BASE | \
+					EMAC_M1_OPB_CLK_66 | \
+					EMAC_M1_TX_MWSW)
+#define EMAC_RMR_DEFAULT		(EMAC_RMR_BASE | \
+					EMAC_RMR_RFAF_128_2048)
+#define EMAC_TMR0_XMIT			(EMAC_TMR0_GNP0 | \
+					EMAC_TMR0_TFAE_128_2048)
+#define EMAC_TRTR_DEFAULT		EMAC_TRTR_256
+#else /* !CONFIG_IBM_EMAC4 */
+#define EMAC_M1_DEFAULT			EMAC_M1_BASE
+#define EMAC_RMR_DEFAULT		EMAC_RMR_BASE
+#define EMAC_TMR0_XMIT			EMAC_TMR0_GNP0
+#define EMAC_TRTR_DEFAULT		EMAC_TRTR_1600
+#endif /* CONFIG_IBM_EMAC4 */
+
+/* SoC implementation specific EMAC register defaults */
+#if defined(CONFIG_440GP)
+#define EMAC_RWMR_DEFAULT		0x80009000
+#define EMAC_TMR0_DEFAULT		0x00000000
+#define EMAC_TMR1_DEFAULT		0xf8640000
+#elif defined(CONFIG_440GX)
+#define EMAC_RWMR_DEFAULT		0x1000a200
+#define EMAC_TMR0_DEFAULT		EMAC_TMR0_TFAE_128_2048
+#define EMAC_TMR1_DEFAULT		0x88810000
+#else
+#define EMAC_RWMR_DEFAULT		0x0f002000
+#define EMAC_TMR0_DEFAULT		0x00000000
+#define EMAC_TMR1_DEFAULT		0x380f0000
+#endif /* CONFIG_440GP */
 
 #endif
diff -urN linux-2.4.23-pre5/drivers/net/ibm_emac/ibm_ocp_enet.c linux-2.4.23-pre6/drivers/net/ibm_emac/ibm_ocp_enet.c
--- linux-2.4.23-pre5/drivers/net/ibm_emac/ibm_ocp_enet.c	2003-08-25 04:44:42.000000000 -0700
+++ linux-2.4.23-pre6/drivers/net/ibm_emac/ibm_ocp_enet.c	2003-10-01 10:38:50.000000000 -0700
@@ -674,7 +674,7 @@
 	fep->tx_desc[fep->tx_slot].ctrl = ctrl;
 
 	/* Send the packet out. */
-	out_be32(&emacp->em0tmr0, EMAC_TXM0_GNP0);
+	out_be32(&emacp->em0tmr0, EMAC_TMR0_XMIT);
 
 	if (++fep->tx_slot == NUM_TX_BUFF)
 		fep->tx_slot = 0;
@@ -699,10 +699,9 @@
 	full_duplex = 0;
 	speed = SPEED_10;
 
-	/* set receive fifo to 4k and tx fifo to 2k */
-	mode_reg = EMAC_M1_RFS_4K | EMAC_M1_TX_FIFO_2K | EMAC_M1_APP |
-	    EMAC_M1_TR0_MULTI;
-       
+	/* set mode register 1 defaults */
+	mode_reg = EMAC_M1_DEFAULT;
+
 	/* Read link mode on PHY */
     	if (fep->phy_mii.def->ops->read_link(&fep->phy_mii) == 0) {
 	        /* If an error occurred, we don't deal with it yet */
@@ -871,26 +870,19 @@
 	if (netif_carrier_ok(fep->ndev))
 	    emac_adjust_to_link(fep);
 
-	/* enable broadcast and individual address */
-	out_be32(&emacp->em0rmr, EMAC_RMR_IAE | EMAC_RMR_BAE);
+	/* enable broadcast/individual address and RX FIFO defaults */
+	out_be32(&emacp->em0rmr, EMAC_RMR_DEFAULT);
 
+	/* set transmit request threshold register */
+	out_be32(&emacp->em0trtr, EMAC_TRTR_DEFAULT);
 
         /* Reconfigure multicast */
         __emac_set_multicast_list(fep->ndev);
 
-	/* set transmit request threshold register */
-	out_be32(&emacp->em0trtr, EMAC_TRTR_256);
-
-
-	/* set receive low/high water mark register */
-#ifdef CONFIG_440
-	/* 440GP has a 64 byte burst length */
-	out_be32(&emacp->em0rwmr, 0x80009000);
-	out_be32(&emacp->em0tmr1, 0xf8640000);
-#else
-	/* 405s have a 16 byte burst length */
-	out_be32(&emacp->em0rwmr, 0x0f002000);
-#endif				/* CONFIG_440 */
+	/* Set receiver/transmitter defaults */
+	out_be32(&emacp->em0rwmr, EMAC_RWMR_DEFAULT);
+	out_be32(&emacp->em0tmr0, EMAC_TMR0_DEFAULT);
+	out_be32(&emacp->em0tmr1, EMAC_TMR1_DEFAULT);
 
 	/* set frame gap */
 	out_be32(&emacp->em0ipgvr, CONFIG_IBM_OCP_ENET_GAP);
@@ -989,7 +981,8 @@
                  * latency peaks caused by this code
                  */
                 emac_reset_configure(fep);
-                emac_kick(fep);
+		if (fep->opened)
+                	emac_kick(fep);
 	} else {
 	        fep->timer_ticks = 0;
 	        netif_carrier_off(fep->ndev);
diff -urN linux-2.4.23-pre5/drivers/net/ibm_emac/ibm_ocp_mal.c linux-2.4.23-pre6/drivers/net/ibm_emac/ibm_ocp_mal.c
--- linux-2.4.23-pre5/drivers/net/ibm_emac/ibm_ocp_mal.c	2003-08-25 04:44:42.000000000 -0700
+++ linux-2.4.23-pre6/drivers/net/ibm_emac/ibm_ocp_mal.c	2003-10-01 10:38:50.000000000 -0700
@@ -103,17 +103,17 @@
 	case 0:
 		set_mal_dcrn(mal, DCRN_MALRCBS0, size);
 		break;
-#ifdef DCRN_RCBS1
+#ifdef DCRN_MALRCBS1
 	case 1:
 		set_mal_dcrn(mal, DCRN_MALRCBS1, size);
 		break;
 #endif
-#ifdef DCRN_RCBS2
+#ifdef DCRN_MALRCBS2
 	case 2:
 		set_mal_dcrn(mal, DCRN_MALRCBS2, size);
 		break;
 #endif
-#ifdef DCRN_RCBS3
+#ifdef DCRN_MALRCBS3
 	case 3:
 		set_mal_dcrn(mal, DCRN_MALRCBS3, size);
 		break;
diff -urN linux-2.4.23-pre5/drivers/net/net_init.c linux-2.4.23-pre6/drivers/net/net_init.c
--- linux-2.4.23-pre5/drivers/net/net_init.c	2003-10-01 10:38:42.000000000 -0700
+++ linux-2.4.23-pre6/drivers/net/net_init.c	2003-10-01 10:38:50.000000000 -0700
@@ -420,7 +420,7 @@
 	dev->hard_header_len 	= ETH_HLEN;
 	dev->mtu		= 1500; /* eth_mtu */
 	dev->addr_len		= ETH_ALEN;
-	dev->tx_queue_len	= 100;	/* Ethernet wants good queues */	
+	dev->tx_queue_len	= 1000;	/* Ethernet wants good queues */	
 	
 	memset(dev->broadcast,0xFF, ETH_ALEN);
 
diff -urN linux-2.4.23-pre5/drivers/net/tlan.c linux-2.4.23-pre6/drivers/net/tlan.c
--- linux-2.4.23-pre5/drivers/net/tlan.c	2003-08-25 04:44:42.000000000 -0700
+++ linux-2.4.23-pre6/drivers/net/tlan.c	2003-10-01 10:38:52.000000000 -0700
@@ -881,7 +881,9 @@
 			err );
 	}
 	dev->addr_len = 6;
-	
+
+	netif_carrier_off(dev);
+
 	/* Device methods */
 	dev->open = &TLan_Open;
 	dev->hard_start_xmit = &TLan_StartTx;
@@ -2205,6 +2207,8 @@
 
 	priv->tlanFullDuplex = FALSE;
 	priv->phyOnline=0;
+	netif_carrier_off(dev);
+
 /*  1.	Assert reset bit. */
 
 	data = inl(dev->base_addr + TLAN_HOST_CMD);
@@ -2368,6 +2372,7 @@
 		}
 		outl( priv->rxListDMA, dev->base_addr + TLAN_CH_PARM );
 		outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
+		netif_carrier_on(dev);
 	} else {
 		printk( "TLAN: %s: Link inactive, will retry in 10 secs...\n", dev->name );
 		TLan_SetTimer( dev, (10*HZ), TLAN_TIMER_FINISH_RESET );
diff -urN linux-2.4.23-pre5/drivers/net/wan/8253x/build linux-2.4.23-pre6/drivers/net/wan/8253x/build
--- linux-2.4.23-pre5/drivers/net/wan/8253x/build	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/drivers/net/wan/8253x/build	2003-10-01 10:38:52.000000000 -0700
@@ -0,0 +1,8 @@
+cc -g  -o 8253xcfg -I. -U__KERNEL__ 8253xcfg.c
+cc -g  -o 8253xspeed -I. -U__KERNEL__ 8253xspeed.c
+cc -g  -o 8253xmode -I. -U__KERNEL__ 8253xmode.c
+cc -g  -o 8253xpeer -I. -U__KERNEL__ 8253xpeer.c
+cc -g  -o eprom9050 -I. -U__KERNEL__ eprom9050.c
+
+
+
diff -urN linux-2.4.23-pre5/drivers/net/wireless/airo.c linux-2.4.23-pre6/drivers/net/wireless/airo.c
--- linux-2.4.23-pre5/drivers/net/wireless/airo.c	2003-10-01 10:38:42.000000000 -0700
+++ linux-2.4.23-pre6/drivers/net/wireless/airo.c	2003-10-01 10:38:52.000000000 -0700
@@ -202,7 +202,7 @@
 #ifndef RUN_AT
 #define RUN_AT(x) (jiffies+(x))
 #endif
-#if LINUX_VERSION_CODE < 0x020500
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,23)
 static inline struct proc_dir_entry *PDE(const struct inode *inode)
 {
 	return inode->u.generic_ip;
diff -urN linux-2.4.23-pre5/drivers/pci/pci.c linux-2.4.23-pre6/drivers/pci/pci.c
--- linux-2.4.23-pre5/drivers/pci/pci.c	2003-10-01 10:38:42.000000000 -0700
+++ linux-2.4.23-pre6/drivers/pci/pci.c	2003-10-01 10:38:53.000000000 -0700
@@ -1475,17 +1475,22 @@
 	u8 hdr_type;
 
 	for (func = 0; func < 8; func++, temp->devfn++) {
-		if (func && !is_multi)		/* not a multi-function device */
-			continue;
 		if (pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type))
 			continue;
 		temp->hdr_type = hdr_type & 0x7f;
 
 		dev = pci_scan_device(temp);
-		if (!dev)
-			continue;
+		if (!pcibios_scan_all_fns() && func == 0) {
+			if (!dev)
+				break;
+		} else {
+			if (!dev)
+				continue;
+			is_multi = 1;
+		}
+
 		pci_name_device(dev);
-		if (!func) {
+		if (!first_dev) {
 			is_multi = hdr_type & 0x80;
 			first_dev = dev;
 		}
@@ -1499,6 +1504,14 @@
 
 		/* Fix up broken headers */
 		pci_fixup_device(PCI_FIXUP_HEADER, dev);
+
+		/*
+		 * If this is a single function device
+		 * don't scan past the first function.
+		 */
+		if (!is_multi)
+			break;
+
 	}
 	return first_dev;
 }
diff -urN linux-2.4.23-pre5/drivers/scsi/megaraid.c linux-2.4.23-pre6/drivers/scsi/megaraid.c
--- linux-2.4.23-pre5/drivers/scsi/megaraid.c	2003-08-25 04:44:42.000000000 -0700
+++ linux-2.4.23-pre6/drivers/scsi/megaraid.c	2003-10-01 10:38:55.000000000 -0700
@@ -9,7 +9,7 @@
  *              as published by the Free Software Foundation; either version
  *              2 of the License, or (at your option) any later version.
  *
- * Version : v1.18f (Dec 10, 2002)
+ * Version : v1.18k (Aug 28, 2003)
  *
  * Description: Linux device driver for LSI Logic MegaRAID controller
  *
@@ -512,6 +512,54 @@
  *
  * remove GFP_DMA flag for ioctl. This was causing overrun of DMA buffers.
  *
+ * Version 1.18g
+ * Fri Jan 31 18:29:25 EST 2003 - Atul Mukker <atul.mukker@lsil.com>
+ *
+ * Write the interrupt valid signature 0x10001234 as soon as reading it to
+ * flush memory caches.
+ *
+ * While sending back the inquiry information, check if the original request
+ * had an associated scatter-gather list and tranfer data from bounce buffer
+ * accordingly.
+ *
+ * Version 1.18h
+ * Thu Feb  6 17:18:48 EST 2003 - Atul Mukker <atul.mukker@lsil.com>
+ *
+ * Reduce the number of sectors per command to 128 from original value of
+ * 1024. Big IO sizes along with certain other operation going on in parallel,
+ * e.g., check consistency and rebuild put a heavy constraint on fW resources
+ * resulting in aborted commands.
+ *
+ * Version 1.18i
+ * Fri Jun 20 07:39:05 EDT 2003 - Atul Mukker <atulm@lsil.com>
+ *
+ * Request and reserve memory/IO regions. Otherwise a panic occurs if 2.00.x
+ * driver is loaded on top of 1.18x driver
+ *
+ * Prevent memory leak in cases when data transfer from/to application fails
+ * and ioctl is failing.
+ *
+ * Set the PCI dma_mask to default value of 0xFFFFFFFF when we get a handle to
+ * it. The previous value of 64-bit might be sticky and would cause the memory
+ * for mailbox and scatter lists to be allocated beyond 4GB. This was observed
+ * on an Itenium
+ *
+ * Version 1.18j
+ * Mon Jul  7 14:39:55 EDT 2003 - Atul Mukker <atulm@lsil.com>
+ *
+ * Disable /proc/megaraid/stat file to prevent buffer overflow error during
+ * read of this file.
+ *
+ * Add support for ioctls on AMD-64 bit platforms
+ *			- Sreenivas Bagalkote <sreenib@lsil.com>
+ *
+ * Version 1.18k
+ * Thu Aug 28 10:05:11 EDT 2003 - Atul Mukker <atulm@lsil.com>
+ *
+ * Make sure to read the correct status and command ids while in ISR. The
+ * numstatus and command id array is invalidated before issuing the commands.
+ * The ISR busy-waits till the correct values are updated in host memory.
+ *
  * BUGS:
  *     Some older 2.1 kernels (eg. 2.1.90) have a bug in pci.c that
  *     fails to detect the controller as a pci device on the system.
@@ -575,12 +623,27 @@
 #include <linux/reboot.h>
 #include <linux/init.h>
 
+#ifdef __x86_64__
+#include <asm/ioctl32.h>
+#endif
+
 #include "sd.h"
 #include "scsi.h"
 #include "hosts.h"
 
 #include "megaraid.h"
 
+#ifdef __x86_64__
+/*
+ * The IOCTL cmd received from 32 bit compiled applications
+ */
+
+extern int register_ioctl32_conversion( unsigned int cmd,
+				int(*handler)(unsigned int, unsigned int, unsigned long,
+						struct file* ));
+extern int unregister_ioctl32_conversion( unsigned int cmd );
+#endif
+
 /*
  *================================================================
  *  #Defines
@@ -1141,8 +1204,14 @@
 	switch (SCpnt->cmnd[0]) {
 	case INQUIRY:
 	case READ_CAPACITY:
-		memcpy (SCpnt->request_buffer,
-			pScb->bounce_buffer, SCpnt->request_bufflen);
+		if ( SCpnt->use_sg ) {
+			sgList = (struct scatterlist *)SCpnt->request_buffer;
+			memcpy(sgList[0].address, pScb->bounce_buffer,
+							SCpnt->request_bufflen);
+		} else {
+				memcpy (SCpnt->request_buffer, pScb->bounce_buffer,
+								SCpnt->request_bufflen);
+		}
 		break;
 	}
 #endif
@@ -2202,30 +2271,22 @@
 	megaCfg = (mega_host_config *) devp;
 	mbox = (mega_mailbox *) tmpBox;
 
-	if (megaCfg->host->irq == irq) {
-		if (megaCfg->flag & IN_ISR) {
-			TRACE (("ISR called reentrantly!!\n"));
-			printk ("ISR called reentrantly!!\n");
-		}
-		megaCfg->flag |= IN_ISR;
-
-		if (mega_busyWaitMbox (megaCfg)) {
-			printk (KERN_WARNING "Error: mailbox busy in isr!\n");
-		}
+		IO_LOCK;
 
 		/* Check if a valid interrupt is pending */
 		if (megaCfg->flag & BOARD_QUARTZ) {
 			dword = RDOUTDOOR (megaCfg);
 			if (dword != 0x10001234) {
 				/* Spurious interrupt */
-				megaCfg->flag &= ~IN_ISR;
+				IO_UNLOCK;
 				return;
 			}
+			WROUTDOOR (megaCfg, 0x10001234);
 		} else {
 			byte = READ_PORT (megaCfg->host->io_port, INTR_PORT);
 			if ((byte & VALID_INTR_BYTE) == 0) {
 				/* Spurious interrupt */
-				megaCfg->flag &= ~IN_ISR;
+				IO_UNLOCK;
 				return;
 			}
 			WRITE_PORT (megaCfg->host->io_port, INTR_PORT, byte);
@@ -2234,58 +2295,27 @@
 		for (idx = 0; idx < MAX_FIRMWARE_STATUS; idx++)
 			completed[idx] = 0;
 
-		IO_LOCK;
 
 		megaCfg->nInterrupts++;
-		qCnt = 0xff;
 		while ((qCnt = megaCfg->mbox->numstatus) == 0xFF) ;
-
-		qStatus = 0xff;
-		while ((qStatus = megaCfg->mbox->status) == 0xFF) ;
+		megaCfg->mbox->numstatus = 0xFF;
 
 		/* Get list of completed requests */
 		for (idx = 0; idx < qCnt; idx++) {
-			while ((sIdx = megaCfg->mbox->completed[idx]) == 0xFF) {
-				printk ("p");
-			}
-			completed[idx] = sIdx;
-			sIdx = 0xFF;
+			while ((completed[idx] = megaCfg->mbox->completed[idx]) == 0xFF);
+			megaCfg->mbox->completed[idx] = 0xFF;
 		}
 
+		qStatus = megaCfg->mbox->status;
+
 		if (megaCfg->flag & BOARD_QUARTZ) {
-			WROUTDOOR (megaCfg, dword);
 			/* Acknowledge interrupt */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
-			/* In this case mbox contains physical address */
-#if 0
-			WRINDOOR (megaCfg, megaCfg->adjdmahandle64 | 0x2);
-#else
-			WRINDOOR (megaCfg, 0x2);
-#endif
-
-#else
-
-#if 0
-			WRINDOOR (megaCfg, virt_to_bus (megaCfg->mbox) | 0x2);
-#else
 			WRINDOOR (megaCfg, 0x2);
-#endif
-
-#endif
-
-#if 0
 			while (RDINDOOR (megaCfg) & 0x02) ;
-#endif
 		} else {
 			CLEAR_INTR (megaCfg->host->io_port);
 		}
 
-#if DEBUG
-		if (qCnt >= MAX_FIRMWARE_STATUS) {
-			printk ("megaraid_isr: cmplt=%d ", qCnt);
-		}
-#endif
-
 		for (idx = 0; idx < qCnt; idx++) {
 			sIdx = completed[idx];
 			if ((sIdx > 0) && (sIdx <= MAX_COMMANDS)) {
@@ -2355,26 +2385,24 @@
 		mega_runpendq (megaCfg);
 		IO_UNLOCK;
 
-	}
-
 }
 
 /*==================================================*/
 /* Wait until the controller's mailbox is available */
 /*==================================================*/
 
-static int mega_busyWaitMbox (mega_host_config * megaCfg)
+static inline int mega_busyWaitMbox (mega_host_config * megaCfg)
 {
 	mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox;
 	long counter;
 
-	for (counter = 0; counter < 10000; counter++) {
+	for (counter = 0; counter < 10; counter++) {
 		if (!mbox->busy) {
 			return 0;
 		}
-		udelay (100);
+		udelay (1);
 	}
-	return -1;		/* give up after 1 second */
+	return -1;		/* give up after 10 usecs */
 }
 
 /*=====================================================
@@ -2409,6 +2437,7 @@
 	u32 phys_mbox;
 #endif
 	u8 retval = -1;
+	int	i;
 
 	mboxData[0x1] = (pScb ? pScb->idx + 1 : 0xFE);	/* Set cmdid */
 	mboxData[0xF] = 1;	/* Set busy */
@@ -2420,25 +2449,8 @@
 	phys_mbox = virt_to_bus (megaCfg->mbox);
 #endif
 
-#if DEBUG
-	ShowMbox (pScb);
-#endif
-
 	/* Wait until mailbox is free */
 	if (mega_busyWaitMbox (megaCfg)) {
-		printk ("Blocked mailbox......!!\n");
-		udelay (1000);
-
-#if DEBUG
-		showMbox (pLastScb);
-#endif
-
-		/* Abort command */
-		if (pScb == NULL) {
-			TRACE (("NULL pScb in megaIssue\n"));
-			printk ("NULL pScb in megaIssue\n");
-		}
-		mega_cmd_done (megaCfg, pScb, 0x08);
 		return -1;
 	}
 
@@ -2486,13 +2498,10 @@
 			WRINDOOR (megaCfg, phys_mbox | 0x1);
 
 			while (mbox->numstatus == 0xFF) ;
-			while (mbox->status == 0xFF) ;
 			while (mbox->mraid_poll != 0x77) ;
 			mbox->mraid_poll = 0;
 			mbox->mraid_ack = 0x77;
-
-			/* while ((cmdDone = RDOUTDOOR (megaCfg)) != 0x10001234);
-			   WROUTDOOR (megaCfg, cmdDone); */
+			mbox->numstatus = 0xFF;
 
 			if (pScb) {
 				mega_cmd_done (megaCfg, pScb, mbox->status);
@@ -2520,15 +2529,14 @@
 				TRACE (("Error: NULL pScb!\n"));
 			}
 		}
+
+		for (i = 0; i < MAX_FIRMWARE_STATUS; i++) {
+				mbox->completed[i] = 0xFF;
+		}
+
 		enable_irq (megaCfg->host->irq);
 		retval = mbox->status;
 	}
-#if DEBUG
-	while (mega_busyWaitMbox (megaCfg)) {
-		printk(KERN_ERR "Blocked mailbox on exit......!\n");
-		udelay (1000);
-	}
-#endif
 
 	return retval;
 }
@@ -2964,15 +2972,15 @@
 
 	int		i, j;
 
-#if BITS_PER_LONG==64
-	u64 megaBase;
-#else
-	u32 megaBase;
-#endif
+	unsigned long megaBase;
+	unsigned long tbase;
 
 	u16 pciIdx = 0;
 	u16 numFound = 0;
 	u16 subsysid, subsysvid;
+	u8 did_mem_map_f = 0;
+	u8 did_io_map_f = 0;
+	u8 did_scsi_register_f = 0;
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)	/* 0x20100 */
 	while (!pcibios_find_device
@@ -2991,6 +2999,17 @@
 		pciBus = pdev->bus->number;
 		pciDevFun = pdev->devfn;
 #endif
+
+		/*
+		 * Set the dma_mask to default value. It might be sticky from previous
+		 * insmod-rmmod sequence
+		 */
+		pdev->dma_mask = 0xFFFFFFFF;
+
+		did_mem_map_f = 0;
+		did_io_map_f = 0;
+		did_scsi_register_f = 0;
+
 		if ((flag & BOARD_QUARTZ) && (skip_id == -1)) {
 				if( (pciVendor == PCI_VENDOR_ID_PERC4_DI_YSTONE &&
 					pciDev == PCI_DEVICE_ID_PERC4_DI_YSTONE) ||
@@ -3041,6 +3060,7 @@
 			if( (subsysvid != AMI_SUBSYS_ID) &&
 					(subsysvid != DELL_SUBSYS_ID) &&
 					(subsysvid != LSI_SUBSYS_ID) &&
+					(subsysvid != INTEL_SUBSYS_ID) &&
 					(subsysvid != HP_SUBSYS_ID) ) continue;
 
 		}
@@ -3065,22 +3085,55 @@
 		megaIrq = pdev->irq;
 #endif
 
+		tbase = megaBase;
+
 		pciIdx++;
 
 		if (flag & BOARD_QUARTZ) {
+
 			megaBase &= PCI_BASE_ADDRESS_MEM_MASK;
+
+			if( ! request_mem_region(megaBase, 128,
+									"MegaRAID: LSI Logic Corporation" ) ) {
+
+					printk(KERN_WARNING "megaraid: mem region busy!\n");
+
+					continue;
+			}
+
 			megaBase = (long) ioremap (megaBase, 128);
-			if (!megaBase)
+
+			if (!megaBase) {
+
+				printk(KERN_WARNING "megaraid: could not map hba memory!\n");
+
+				release_mem_region(tbase, 128);
+
 				continue;
+			}
+			did_mem_map_f = 1;
+
 		} else {
 			megaBase &= PCI_BASE_ADDRESS_IO_MASK;
 			megaBase += 0x10;
+
+			if( ! request_region(megaBase, 16,
+									"MegaRAID: LSI Logic Corporation") ) {
+
+					printk(KERN_WARNING "megaraid: region busy.\n");
+
+					continue;
+			}
+			did_io_map_f = 1;
+
 		}
 
 		/* Initialize SCSI Host structure */
 		host = scsi_register (pHostTmpl, sizeof (mega_host_config));
 		if (!host)
-			goto err_unmap;
+			goto fail_attach;
+
+		did_scsi_register_f = 1;
 
 		/*
 		 * Comment the following initialization if you know 'max_sectors' is
@@ -3088,7 +3141,7 @@
 		 * This field was introduced in Linus's kernel 2.4.7pre3 and it
 		 * greatly increases the IO performance - AM
 		 */
-		host->max_sectors = 1024;
+		host->max_sectors = 128;
 
 		scsi_set_pci_device(host, pdev);
 		megaCfg = (mega_host_config *) host->hostdata;
@@ -3130,11 +3183,8 @@
 		megaCfg->host->unique_id = (pciBus << 8) | pciDevFun;
 		megaCtlrs[numCtlrs] = megaCfg;
 
-		if (!(flag & BOARD_QUARTZ)) {
-
-			/* Request our IO Range */
-			if( !request_region(megaBase, 16, "megaraid") )
-				goto err_unregister;
+		if (flag & BOARD_QUARTZ) {
+				megaCfg->host->base = tbase;
 		}
 
 		/* Request our IRQ */
@@ -3143,7 +3193,7 @@
 			printk (KERN_WARNING
 				"megaraid: Couldn't register IRQ %d!\n",
 				megaIrq);
-			goto err_release;
+			goto fail_attach;
 		}
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
@@ -3265,8 +3315,7 @@
 					     sizeof (mega_mailbox64),
 					     (void *) megaCfg->mailbox64ptr,
 					     megaCfg->dma_handle64);
-			scsi_unregister (host);
-			continue;
+			goto fail_attach;
 		}
 
 		/*
@@ -3301,14 +3350,17 @@
 #endif
 #endif
 		continue;
-	      err_release:
-		if (flag & BOARD_QUARTZ)
-			release_region (megaBase, 16);
-	      err_unregister:
-		scsi_unregister (host);
-	      err_unmap:
-		if (flag & BOARD_QUARTZ)
-			iounmap ((void *) megaBase);
+fail_attach:
+		if( did_mem_map_f ) {
+				iounmap((void *)megaBase);
+				release_mem_region(tbase, 128);
+		}
+		if( did_io_map_f ) {
+				release_region(megaBase, 16);
+		}
+		if( did_scsi_register_f ) {
+				scsi_unregister (host);
+		}
 	}
 	return numFound;
 }
@@ -3408,6 +3460,12 @@
 		}
 
 		init_MUTEX (&mimd_entry_mtx);
+#ifdef __x86_64__
+		/*
+		 * Register the 32-bit ioctl conversion
+		 */
+		register_ioctl32_conversion( MEGAIOCCMD, sys_ioctl );
+#endif
 	}
 
 	return count;
@@ -3439,6 +3497,7 @@
 	/* Free our resources */
 	if (megaCfg->flag & BOARD_QUARTZ) {
 		iounmap ((void *) megaCfg->base);
+		release_mem_region(megaCfg->host->base, 128);
 	} else {
 		release_region (megaCfg->host->io_port, 16);
 	}
@@ -3485,6 +3544,9 @@
 
 	unregister_chrdev (major, "megadev");
 	unregister_reboot_notifier (&mega_notifier);
+#ifdef __x86_64__
+	unregister_ioctl32_conversion( MEGAIOCCMD );
+#endif
 
 	return 0;
 }
@@ -4235,7 +4297,6 @@
 static int proc_read_stat (char *page, char **start, off_t offset,
 		int count, int *eof, void *data)
 {
-	int i;
 	mega_host_config *megaCfg = (mega_host_config *) data;
 
 	*start = page;
@@ -4244,6 +4305,11 @@
 	proc_printf (megaCfg, "Interrupts Collected = %lu\n",
 		     megaCfg->nInterrupts);
 
+	proc_printf (megaCfg, "INTERFACE DISABLED\n");
+	COPY_BACK;
+	return count;
+
+#if 0	// can cause buffer overrun with 40 logical drives and IO information
 	for (i = 0; i < megaCfg->numldrv; i++) {
 		proc_printf (megaCfg, "Logical Drive %d:\n", i);
 
@@ -4259,6 +4325,7 @@
 
 	COPY_BACK;
 	return count;
+#endif
 }
 
 static int proc_read_status (char *page, char **start, off_t offset,
@@ -4896,16 +4963,16 @@
 			if( kvaddr == NULL ) {
 				printk(KERN_WARNING "megaraid:allocation failed\n");
 				ret = -ENOMEM;
-				goto out;
+				goto out_ioctl_cmd_new;
 			}
 
 			ioc.ui.fcs.buffer = kvaddr;
 
 			if (inlen) {
 				/* copyin the user data */
-				if (copy_from_user(kvaddr, (char *)uaddr, length )) {
-					ret = -EFAULT;
-					goto out;
+				if( copy_from_user(kvaddr, (char *)uaddr, length ) ) {
+						ret = -EFAULT;
+						goto out_ioctl_cmd_new;
 				}
 			}
 		}
@@ -4923,10 +4990,9 @@
 		down(&mimd_ioctl_sem);
 
 		if( !scsicmd->result && outlen ) {
-			if (copy_to_user(uaddr, kvaddr, length))
-			{
-				ret = -EFAULT;
-				goto out;
+			if (copy_to_user(uaddr, kvaddr, length)) {
+				return -EFAULT;
+				goto out_ioctl_cmd_new;
 			}
 		}
 
@@ -4946,7 +5012,8 @@
 			put_user (scsicmd->result, &uioc->mbox[17]);
 		}
 
-out:
+out_ioctl_cmd_new:
+
 		if (kvaddr) {
 			dma_free_consistent(pdevp, length, kvaddr, dma_addr);
 		}
@@ -5047,12 +5114,8 @@
 
 			if( kvaddr == NULL ) {
 				printk (KERN_WARNING "megaraid:allocation failed\n");
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)	/*0x20400 */
-				kfree(scsicmd);
-#else
-				scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd));
-#endif
-				return -ENOMEM;
+				ret = -ENOMEM;
+				goto out_ioctl_cmd;
 			}
 
 			ioc.data = kvaddr;
@@ -5060,9 +5123,15 @@
 			if (inlen) {
 				if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) {
 					/* copyin the user data */
-					copy_from_user (kvaddr, uaddr, ioc.pthru.dataxferlen);
+					if( copy_from_user (kvaddr, uaddr, ioc.pthru.dataxferlen)){
+							ret = -EFAULT;
+							goto out_ioctl_cmd;
+					}
 				} else {
-					copy_from_user (kvaddr, uaddr, inlen);
+					if( copy_from_user (kvaddr, uaddr, inlen) ) {
+							ret = -EFAULT;
+							goto out_ioctl_cmd;
+					}
 				}
 			}
 		}
@@ -5080,11 +5149,15 @@
 
 		if (!scsicmd->result && outlen) {
 			if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) {
-				if (copy_to_user (uaddr, kvaddr, ioc.pthru.dataxferlen))	
+				if (copy_to_user (uaddr, kvaddr, ioc.pthru.dataxferlen)) {
 					ret = -EFAULT;
+					goto out_ioctl_cmd;
+				}
 			} else {
-				if (copy_to_user (uaddr, kvaddr, outlen)) 
+				if (copy_to_user (uaddr, kvaddr, outlen)) {
 					ret = -EFAULT;
+					goto out_ioctl_cmd;
+				}
 			}
 		}
 
@@ -5109,6 +5182,8 @@
 			put_user (scsicmd->result, &uioc->mbox[17]); /* status */
 		}
 
+out_ioctl_cmd:
+
 		if (kvaddr) {
 			dma_free_consistent(pdevp, PAGE_SIZE, kvaddr, dma_addr );
 		}
diff -urN linux-2.4.23-pre5/drivers/scsi/megaraid.h linux-2.4.23-pre6/drivers/scsi/megaraid.h
--- linux-2.4.23-pre5/drivers/scsi/megaraid.h	2003-06-13 07:51:36.000000000 -0700
+++ linux-2.4.23-pre6/drivers/scsi/megaraid.h	2003-10-01 10:38:55.000000000 -0700
@@ -30,7 +30,8 @@
 #define M_RD_IOCTL_CMD_NEW		0x81
 #define M_RD_DRIVER_IOCTL_INTERFACE	0x82
 
-#define MEGARAID_VERSION "v1.18f (Release Date: Tue Dec 10 09:54:39 EST 2002)\n"
+#define MEGARAID_VERSION "v1.18k (Release Date: Thu Aug 28 10:05:11 EDT 2003)\n"
+
 
 #define MEGARAID_IOCTL_VERSION 	114
 
@@ -190,6 +191,7 @@
 #define	AMI_SUBSYS_ID			0x101E
 #define DELL_SUBSYS_ID			0x1028
 #define	HP_SUBSYS_ID			0x103C
+#define	INTEL_SUBSYS_ID			0x8086
 
 #define AMI_SIGNATURE	      		0x3344
 #define AMI_SIGNATURE_471	  	0xCCCC
@@ -899,7 +901,9 @@
  * Defines for Driver IOCTL interface, Op-code:M_RD_DRIVER_IOCTL_INTERFACE
  */
 #define MEGAIOC_MAGIC  	'm'
-#define MEGAIOCCMD     	_IOWR(MEGAIOC_MAGIC, 0)	/* Mega IOCTL command */
+
+/* Mega IOCTL command */
+#define MEGAIOCCMD     	_IOWR(MEGAIOC_MAGIC, 0, struct uioctl_t)
 
 #define MEGAIOC_QNADAP		'm'	/* Query # of adapters */
 #define MEGAIOC_QDRVRVER	'e'	/* Query driver version */
diff -urN linux-2.4.23-pre5/drivers/sound/.indent.pro linux-2.4.23-pre6/drivers/sound/.indent.pro
--- linux-2.4.23-pre5/drivers/sound/.indent.pro	1997-09-30 08:46:46.000000000 -0700
+++ linux-2.4.23-pre6/drivers/sound/.indent.pro	1969-12-31 16:00:00.000000000 -0800
@@ -1,8 +0,0 @@
--bad 
--bap 
--nfca
--bl 
--psl 
--di16
--lp
--ip5
diff -urN linux-2.4.23-pre5/drivers/sound/.version linux-2.4.23-pre6/drivers/sound/.version
--- linux-2.4.23-pre5/drivers/sound/.version	1997-11-09 23:01:54.000000000 -0800
+++ linux-2.4.23-pre6/drivers/sound/.version	1969-12-31 16:00:00.000000000 -0800
@@ -1,2 +0,0 @@
-3.8s
-0x030804
diff -urN linux-2.4.23-pre5/drivers/video/vesafb.c linux-2.4.23-pre6/drivers/video/vesafb.c
--- linux-2.4.23-pre5/drivers/video/vesafb.c	2003-08-25 04:44:43.000000000 -0700
+++ linux-2.4.23-pre6/drivers/video/vesafb.c	2003-10-01 10:38:57.000000000 -0700
@@ -93,7 +93,11 @@
 } fbcon_cmap;
 
 static int             inverse   = 0;
+#ifdef __x86_64__
+static int             mtrr      = 1;
+#else
 static int             mtrr      = 0;
+#endif
 static int	 vram __initdata = 0;	/* needed for vram boot option */
 static int             currcon   = 0;
 
diff -urN linux-2.4.23-pre5/fs/proc/array.c linux-2.4.23-pre6/fs/proc/array.c
--- linux-2.4.23-pre5/fs/proc/array.c	2003-06-13 07:51:37.000000000 -0700
+++ linux-2.4.23-pre6/fs/proc/array.c	2003-10-01 10:38:57.000000000 -0700
@@ -70,6 +70,7 @@
 #include <linux/smp.h>
 #include <linux/signal.h>
 #include <linux/highmem.h>
+#include <linux/seq_file.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -511,179 +512,96 @@
 		       size, resident, share, trs, lrs, drs, dt);
 }
 
-/*
- * The way we support synthetic files > 4K
- * - without storing their contents in some buffer and
- * - without walking through the entire synthetic file until we reach the
- *   position of the requested data
- * is to cleverly encode the current position in the file's f_pos field.
- * There is no requirement that a read() call which returns `count' bytes
- * of data increases f_pos by exactly `count'.
- *
- * This idea is Linus' one. Bruno implemented it.
- */
-
-/*
- * For the /proc/<pid>/maps file, we use fixed length records, each containing
- * a single line.
- *
- * f_pos = (number of the vma in the task->mm->mmap list) * PAGE_SIZE
- *         + (index into the line)
- */
-/* for systems with sizeof(void*) == 4: */
-#define MAPS_LINE_FORMAT4	  "%08lx-%08lx %s %08lx %s %lu"
-#define MAPS_LINE_MAX4	49 /* sum of 8  1  8  1 4 1 8 1 5 1 10 1 */
-
-/* for systems with sizeof(void*) == 8: */
-#define MAPS_LINE_FORMAT8	  "%016lx-%016lx %s %016lx %s %lu"
-#define MAPS_LINE_MAX8	73 /* sum of 16  1  16  1 4 1 16 1 5 1 10 1 */
-
-#define MAPS_LINE_FORMAT	(sizeof(void*) == 4 ? MAPS_LINE_FORMAT4 : MAPS_LINE_FORMAT8)
-#define MAPS_LINE_MAX	(sizeof(void*) == 4 ?  MAPS_LINE_MAX4 :  MAPS_LINE_MAX8)
-
-static int proc_pid_maps_get_line (char *buf, struct vm_area_struct *map)
-{
-	/* produce the next line */
-	char *line;
-	char str[5];
-	int flags;
-	kdev_t dev;
-	unsigned long ino;
+static int show_map(struct seq_file *m, void *v)
+{
+	struct vm_area_struct *map = v;
+	struct file *file = map->vm_file;
+	int flags = map->vm_flags;
+	unsigned long ino = 0;
+	dev_t dev = 0;
 	int len;
 
-	flags = map->vm_flags;
-
-	str[0] = flags & VM_READ ? 'r' : '-';
-	str[1] = flags & VM_WRITE ? 'w' : '-';
-	str[2] = flags & VM_EXEC ? 'x' : '-';
-	str[3] = flags & VM_MAYSHARE ? 's' : 'p';
-	str[4] = 0;
-
-	dev = 0;
-	ino = 0;
-	if (map->vm_file != NULL) {
-		dev = map->vm_file->f_dentry->d_inode->i_dev;
-		ino = map->vm_file->f_dentry->d_inode->i_ino;
-		line = d_path(map->vm_file->f_dentry,
-			      map->vm_file->f_vfsmnt,
-			      buf, PAGE_SIZE);
-		if (IS_ERR(line))
-			return PTR_ERR(line);
-		buf[PAGE_SIZE-1] = '\n';
-		line -= MAPS_LINE_MAX;
-		if(line < buf)
-			line = buf;
-	} else
-		line = buf;
-
-	len = sprintf(line,
-		      MAPS_LINE_FORMAT,
-		      map->vm_start, map->vm_end, str, map->vm_pgoff << PAGE_SHIFT,
-		      kdevname(dev), ino);
-
-	if(map->vm_file) {
-		int i;
-		for(i = len; i < MAPS_LINE_MAX; i++)
-			line[i] = ' ';
-		len = buf + PAGE_SIZE - line;
-		memmove(buf, line, len);
-	} else
-		line[len++] = '\n';
-	return len;
+	if (file) {
+		struct inode *inode = map->vm_file->f_dentry->d_inode;
+		dev = kdev_t_to_nr(inode->i_sb->s_dev);
+		ino = inode->i_ino;
+	}
+
+	seq_printf(m, "%08lx-%08lx %c%c%c%c %08lx %02x:%02x %lu %n",
+			map->vm_start,
+			map->vm_end,
+			flags & VM_READ ? 'r' : '-',
+			flags & VM_WRITE ? 'w' : '-',
+			flags & VM_EXEC ? 'x' : '-',
+			flags & VM_MAYSHARE ? 's' : 'p',
+			map->vm_pgoff << PAGE_SHIFT,
+			MAJOR(dev), MINOR(dev), ino, &len);
+
+	if (map->vm_file) {
+		len = 25 + sizeof(void*) * 6 - len;
+		if (len < 1)
+			len = 1;
+		seq_printf(m, "%*c", len, ' ');
+		seq_path(m, file->f_vfsmnt, file->f_dentry, " \t\n\\");
+	}
+	seq_putc(m, '\n');
+	return 0;
 }
 
-ssize_t proc_pid_read_maps (struct task_struct *task, struct file * file, char * buf,
-			  size_t count, loff_t *ppos)
+static void *m_start(struct seq_file *m, loff_t *pos)
 {
+	struct task_struct *task = m->private;
 	struct mm_struct *mm;
 	struct vm_area_struct * map;
-	char *tmp, *kbuf;
-	long retval;
-	int off, lineno, loff;
-
-	/* reject calls with out of range parameters immediately */
-	retval = 0;
-	if (*ppos > LONG_MAX)
-		goto out;
-	if (count == 0)
-		goto out;
-	off = (long)*ppos;
-	/*
-	 * We might sleep getting the page, so get it first.
-	 */
-	retval = -ENOMEM;
-	kbuf = (char*)__get_free_page(GFP_KERNEL);
-	if (!kbuf)
-		goto out;
-
-	tmp = (char*)__get_free_page(GFP_KERNEL);
-	if (!tmp)
-		goto out_free1;
+	loff_t l = *pos;
 
 	task_lock(task);
 	mm = task->mm;
 	if (mm)
 		atomic_inc(&mm->mm_users);
 	task_unlock(task);
-	retval = 0;
+
 	if (!mm)
-		goto out_free2;
+		return NULL;
 
 	down_read(&mm->mmap_sem);
 	map = mm->mmap;
-	lineno = 0;
-	loff = 0;
-	if (count > PAGE_SIZE)
-		count = PAGE_SIZE;
-	while (map) {
-		int len;
-		if (off > PAGE_SIZE) {
-			off -= PAGE_SIZE;
-			goto next;
-		}
-		len = proc_pid_maps_get_line(tmp, map);
-		if (len < 0)
-			goto out_unlock;
-		len -= off;
-		if (len > 0) {
-			if (retval+len > count) {
-				/* only partial line transfer possible */
-				len = count - retval;
-				/* save the offset where the next read
-				 * must start */
-				loff = len+off;
-			}
-			memcpy(kbuf+retval, tmp+off, len);
-			retval += len;
-		}
-		off = 0;
-next:
-		if (!loff)
-			lineno++;
-		if (retval >= count)
-			break;
-		if (loff) BUG();
+	while (l-- && map)
 		map = map->vm_next;
+	if (!map) {
+		up_read(&mm->mmap_sem);
+		mmput(mm);
 	}
+	return map;
+}
 
-out_unlock:
-	up_read(&mm->mmap_sem);
-	mmput(mm);
+static void m_stop(struct seq_file *m, void *v)
+{
+	struct vm_area_struct *map = v;
+	if (map) {
+		struct mm_struct *mm = map->vm_mm;
+		up_read(&mm->mmap_sem);
+		mmput(mm);
+	}
+}
 
-	if (retval > count) BUG();
-	if (copy_to_user(buf, kbuf, retval))
-		retval = -EFAULT;
-	else
-		*ppos = (lineno << PAGE_SHIFT) + loff;
-
-out_free2:
-	free_page((unsigned long)tmp);
-out_free1:
-	free_page((unsigned long)kbuf);
-out:
-	return retval;
+static void *m_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	struct vm_area_struct *map = v;
+	(*pos)++;
+	if (map->vm_next)
+		return map->vm_next;
+	m_stop(m, v);
+	return NULL;
 }
 
+struct seq_operations proc_pid_maps_op = {
+	.start	= m_start,
+	.next	= m_next,
+	.stop	= m_stop,
+	.show	= show_map
+};
+
 #ifdef CONFIG_SMP
 int proc_pid_cpu(struct task_struct *task, char * buffer)
 {
diff -urN linux-2.4.23-pre5/fs/proc/base.c linux-2.4.23-pre6/fs/proc/base.c
--- linux-2.4.23-pre5/fs/proc/base.c	2003-08-25 04:44:43.000000000 -0700
+++ linux-2.4.23-pre6/fs/proc/base.c	2003-10-01 10:38:57.000000000 -0700
@@ -36,7 +36,6 @@
 
 #define fake_ino(pid,ino) (((pid)<<16)|(ino))
 
-ssize_t proc_pid_read_maps(struct task_struct*,struct file*,char*,size_t,loff_t*);
 int proc_pid_stat(struct task_struct*,char*);
 int proc_pid_status(struct task_struct*,char*);
 int proc_pid_statm(struct task_struct*,char*);
@@ -269,19 +268,23 @@
 	return proc_check_root(inode);
 }
 
-static ssize_t pid_maps_read(struct file * file, char * buf,
-			      size_t count, loff_t *ppos)
+extern struct seq_operations proc_pid_maps_op;
+static int maps_open(struct inode *inode, struct file *file)
 {
-	struct inode * inode = file->f_dentry->d_inode;
 	struct task_struct *task = inode->u.proc_i.task;
-	ssize_t res;
-
-	res = proc_pid_read_maps(task, file, buf, count, ppos);
-	return res;
+	int ret = seq_open(file, &proc_pid_maps_op);
+	if (!ret) {
+		struct seq_file *m = file->private_data;
+		m->private = task;
+	}
+	return ret;
 }
 
 static struct file_operations proc_maps_operations = {
-	read:		pid_maps_read,
+	.open		= maps_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
 };
 
 extern struct seq_operations mounts_op;
diff -urN linux-2.4.23-pre5/fs/proc/generic.c linux-2.4.23-pre6/fs/proc/generic.c
--- linux-2.4.23-pre5/fs/proc/generic.c	2003-06-13 07:51:37.000000000 -0700
+++ linux-2.4.23-pre6/fs/proc/generic.c	2003-10-01 10:38:57.000000000 -0700
@@ -150,13 +150,14 @@
 			offset += file->f_pos;
 	}
 	retval = -EINVAL;
-	if (offset>=0 && offset<=file->f_dentry->d_inode->i_sb->s_maxbytes) {
+	if (offset>=0 && (unsigned long long)offset<=file->f_dentry->d_inode->i_sb->s_maxbytes) {
 		if (offset != file->f_pos) {
 			file->f_pos = offset;
 			file->f_reada = 0;
 		}
 		retval = offset;
 	}
+	/* RED-PEN user can fake an error here by setting offset to >=-4095 && <0  */
 	return retval;
 }
 
diff -urN linux-2.4.23-pre5/fs/proc/inode.c linux-2.4.23-pre6/fs/proc/inode.c
--- linux-2.4.23-pre5/fs/proc/inode.c	2003-06-13 07:51:37.000000000 -0700
+++ linux-2.4.23-pre6/fs/proc/inode.c	2003-10-01 10:38:57.000000000 -0700
@@ -186,6 +186,7 @@
 	s->s_blocksize_bits = 10;
 	s->s_magic = PROC_SUPER_MAGIC;
 	s->s_op = &proc_sops;
+	s->s_maxbytes = ~0UL;
 	
 	root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root);
 	if (!root_inode)
diff -urN linux-2.4.23-pre5/fs/proc/kcore.c linux-2.4.23-pre6/fs/proc/kcore.c
--- linux-2.4.23-pre5/fs/proc/kcore.c	2002-08-02 17:39:45.000000000 -0700
+++ linux-2.4.23-pre6/fs/proc/kcore.c	2003-10-01 10:38:57.000000000 -0700
@@ -27,11 +27,14 @@
 	return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
 }
 
+static loff_t lseek_kcore(struct file * file, loff_t offset, int origin);
+
 static ssize_t read_kcore(struct file *, char *, size_t, loff_t *);
 
 struct file_operations proc_kcore_operations = {
 	read:		read_kcore,
 	open:		open_kcore,
+	llseek:		lseek_kcore,
 };
 
 #ifdef CONFIG_KCORE_AOUT
@@ -112,9 +115,9 @@
 
 extern char saved_command_line[];
 
-static size_t get_kcore_size(int *num_vma, size_t *elf_buflen)
+static unsigned long get_kcore_size(int *num_vma, size_t *elf_buflen)
 {
-	size_t try, size;
+	unsigned long try, size;
 	struct vm_struct *m;
 
 	*num_vma = 0;
@@ -125,7 +128,7 @@
 	}
 
 	for (m=vmlist; m; m=m->next) {
-		try = (size_t)m->addr + m->size;
+		try = (unsigned long)m->addr + m->size;
 		if (try > size)
 			size = try;
 		*num_vma = *num_vma + 1;
@@ -313,14 +316,14 @@
 static ssize_t read_kcore(struct file *file, char *buffer, size_t buflen, loff_t *fpos)
 {
 	ssize_t acc = 0;
-	size_t size, tsz;
+	unsigned long size, tsz;
 	size_t elf_buflen;
 	int num_vma;
 	unsigned long start;
 
 	read_lock(&vmlist_lock);
 	proc_root_kcore->size = size = get_kcore_size(&num_vma, &elf_buflen);
-	if (buflen == 0 || *fpos >= size) {
+	if (buflen == 0 || (unsigned long long)*fpos >= size) {
 		read_unlock(&vmlist_lock);
 		return 0;
 	}
@@ -390,9 +393,16 @@
 	start = PAGE_OFFSET + (*fpos - elf_buflen);
 	if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen)
 		tsz = buflen;
-		
 	while (buflen) {
-		if ((start >= VMALLOC_START) && (start < VMALLOC_END)) {
+		int err; 
+	
+		if ((start > PAGE_OFFSET) && (start < (unsigned long)high_memory)) {
+			if (kern_addr_valid(start)) {
+				err = copy_to_user(buffer, (char *)start, tsz);
+			} else {
+				err = clear_user(buffer, tsz);
+			}
+		} else {
 			char * elf_buf;
 			struct vm_struct *m;
 			unsigned long curstart = start;
@@ -432,24 +442,11 @@
 					(char *)vmstart, vmsize);
 			}
 			read_unlock(&vmlist_lock);
-			if (copy_to_user(buffer, elf_buf, tsz)) {
+			err = copy_to_user(buffer, elf_buf, tsz); 
 				kfree(elf_buf);
-				return -EFAULT;
 			}
-			kfree(elf_buf);
-		} else if ((start > PAGE_OFFSET) && (start < 
-						(unsigned long)high_memory)) {
-			if (kern_addr_valid(start)) {
-				if (copy_to_user(buffer, (char *)start, tsz))
+		if (err)
 					return -EFAULT;
-			} else {
-				if (clear_user(buffer, tsz))
-					return -EFAULT;
-			}
-		} else {
-			if (clear_user(buffer, tsz))
-				return -EFAULT;
-		}
 		buflen -= tsz;
 		*fpos += tsz;
 		buffer += tsz;
@@ -461,3 +458,17 @@
 	return acc;
 }
 #endif /* CONFIG_KCORE_AOUT */
+
+static loff_t lseek_kcore(struct file * file, loff_t offset, int origin)
+{
+	switch (origin) {
+		case 2:
+			offset += file->f_dentry->d_inode->i_size;
+			break;
+		case 1:
+			offset += file->f_pos;
+	}
+	/* RED-PEN user can fake an error here by setting offset to >=-4095 && <0  */
+	file->f_pos = offset;
+	return offset;
+}
diff -urN linux-2.4.23-pre5/include/acpi/acconfig.h linux-2.4.23-pre6/include/acpi/acconfig.h
--- linux-2.4.23-pre5/include/acpi/acconfig.h	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/include/acpi/acconfig.h	2003-10-01 10:38:57.000000000 -0700
@@ -64,7 +64,7 @@
 
 /* Version string */
 
-#define ACPI_CA_VERSION                 0x20030916
+#define ACPI_CA_VERSION                 0x20030918
 
 /* Maximum objects in the various object caches */
 
diff -urN linux-2.4.23-pre5/include/acpi/acdisasm.h linux-2.4.23-pre6/include/acpi/acdisasm.h
--- linux-2.4.23-pre5/include/acpi/acdisasm.h	2003-08-25 04:44:43.000000000 -0700
+++ linux-2.4.23-pre6/include/acpi/acdisasm.h	2003-10-01 10:38:57.000000000 -0700
@@ -152,10 +152,6 @@
 acpi_dm_decode_internal_object (
 	union acpi_operand_object       *obj_desc);
 
-void
-acpi_dm_decode_node (
-	struct acpi_namespace_node      *node);
-
 u32
 acpi_dm_block_type (
 	union acpi_parse_object         *op);
diff -urN linux-2.4.23-pre5/include/acpi/acstruct.h linux-2.4.23-pre6/include/acpi/acstruct.h
--- linux-2.4.23-pre5/include/acpi/acstruct.h	2003-08-25 04:44:43.000000000 -0700
+++ linux-2.4.23-pre6/include/acpi/acstruct.h	2003-10-01 10:38:57.000000000 -0700
@@ -91,11 +91,12 @@
 	struct acpi_namespace_node          arguments[ACPI_METHOD_NUM_ARGS];    /* Control method arguments */
 	union acpi_operand_object           **caller_return_desc;
 	union acpi_generic_state            *control_state;                     /* List of control states (nested IFs) */
+	struct acpi_namespace_node          *deferred_node;                      /* Used when executing deferred opcodes */
 	struct acpi_namespace_node          local_variables[ACPI_METHOD_NUM_LOCALS];    /* Control method locals */
 	struct acpi_namespace_node          *method_call_node;                  /* Called method Node*/
 	union acpi_parse_object             *method_call_op;                    /* method_call Op if running a method */
 	union acpi_operand_object           *method_desc;                       /* Method descriptor if running a method */
-	struct acpi_namespace_node          *method_node;                       /* Method Node if running a method */
+	struct acpi_namespace_node          *method_node;                       /* Method node if running a method. */
 	union acpi_parse_object             *op;                                /* Current parser op */
 	union acpi_operand_object           *operands[ACPI_OBJ_NUM_OPERANDS+1]; /* Operands passed to the interpreter (+1 for NULL terminator) */
 	const struct acpi_opcode_info       *op_info;                           /* Info on current opcode */
diff -urN linux-2.4.23-pre5/include/asm-alpha/irq.h linux-2.4.23-pre6/include/asm-alpha/irq.h
--- linux-2.4.23-pre5/include/asm-alpha/irq.h	2003-08-25 04:44:43.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-alpha/irq.h	2003-10-01 10:38:57.000000000 -0700
@@ -16,7 +16,17 @@
    many places throughout the kernel to size static arrays.  That's ok,
    we'll use alpha_mv.nr_irqs when we want the real thing.  */
 
-# define NR_IRQS	(32768 + 16)	/* marvel - 32 pids */
+/* When LEGACY_START_ADDRESS is selected, we leave out:
+     TITAN
+     WILDFIRE
+     MARVEL
+*/
+
+# if defined(CONFIG_ALPHA_LEGACY_START_ADDRESS)
+#  define NR_IRQS	(128)		/* max is RAWHIDE/TAKARA */
+# else
+#  define NR_IRQS	(32768 + 16)	/* marvel - 32 pids */
+# endif
 
 #elif defined(CONFIG_ALPHA_CABRIOLET) || \
       defined(CONFIG_ALPHA_EB66P)     || \
diff -urN linux-2.4.23-pre5/include/asm-alpha/pci.h linux-2.4.23-pre6/include/asm-alpha/pci.h
--- linux-2.4.23-pre5/include/asm-alpha/pci.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-alpha/pci.h	2003-10-01 10:38:57.000000000 -0700
@@ -50,6 +50,7 @@
    bus numbers.  */
 
 #define pcibios_assign_all_busses()	1
+#define pcibios_scan_all_fns()		0
 
 #define PCIBIOS_MIN_IO		alpha_mv.min_io_address
 #define PCIBIOS_MIN_MEM		alpha_mv.min_mem_address
diff -urN linux-2.4.23-pre5/include/asm-alpha/processor.h linux-2.4.23-pre6/include/asm-alpha/processor.h
--- linux-2.4.23-pre5/include/asm-alpha/processor.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-alpha/processor.h	2003-10-01 10:38:57.000000000 -0700
@@ -56,8 +56,8 @@
 	/*
 	 * The fields below are Linux-specific:
 	 *
-	 * bit 1..5: IEEE_TRAP_ENABLE bits (see fpu.h)
-	 * bit 6..8: UAC bits (see sysinfo.h)
+	 * bit 1..6: IEEE_TRAP_ENABLE bits (see fpu.h)
+	 * bit 7..9: UAC bits (see sysinfo.h)
 	 * bit 17..21: IEEE_STATUS_MASK bits (see fpu.h)
 	 * bit 63: die_if_kernel recursion lock
 	 */
diff -urN linux-2.4.23-pre5/include/asm-alpha/sysinfo.h linux-2.4.23-pre6/include/asm-alpha/sysinfo.h
--- linux-2.4.23-pre5/include/asm-alpha/sysinfo.h	1999-03-21 20:53:46.000000000 -0800
+++ linux-2.4.23-pre6/include/asm-alpha/sysinfo.h	2003-10-01 10:38:57.000000000 -0700
@@ -31,7 +31,7 @@
 
 /* This is the shift that is applied to the UAC bits as stored in the 
    per-thread flags.  */
-#define UAC_SHIFT			6
+#define UAC_SHIFT			7
 
 #endif
 
diff -urN linux-2.4.23-pre5/include/asm-alpha/xor.h linux-2.4.23-pre6/include/asm-alpha/xor.h
--- linux-2.4.23-pre5/include/asm-alpha/xor.h	2000-11-12 19:39:51.000000000 -0800
+++ linux-2.4.23-pre6/include/asm-alpha/xor.h	2003-10-01 10:38:57.000000000 -0700
@@ -32,809 +32,809 @@
 				 unsigned long *, unsigned long *,
 				 unsigned long *, unsigned long *);
 
-asm("
-	.text
-	.align 3
-	.ent xor_alpha_2
-xor_alpha_2:
-	.prologue 0
-	srl $16, 6, $16
-	.align 4
-2:
-	ldq $0,0($17)
-	ldq $1,0($18)
-	ldq $2,8($17)
-	ldq $3,8($18)
-
-	ldq $4,16($17)
-	ldq $5,16($18)
-	ldq $6,24($17)
-	ldq $7,24($18)
-
-	ldq $19,32($17)
-	ldq $20,32($18)
-	ldq $21,40($17)
-	ldq $22,40($18)
-
-	ldq $23,48($17)
-	ldq $24,48($18)
-	ldq $25,56($17)
-	xor $0,$1,$0		# 7 cycles from $1 load
-
-	ldq $27,56($18)
-	xor $2,$3,$2
-	stq $0,0($17)
-	xor $4,$5,$4
-
-	stq $2,8($17)
-	xor $6,$7,$6
-	stq $4,16($17)
-	xor $19,$20,$19
-
-	stq $6,24($17)
-	xor $21,$22,$21
-	stq $19,32($17)
-	xor $23,$24,$23
-
-	stq $21,40($17)
-	xor $25,$27,$25
-	stq $23,48($17)
-	subq $16,1,$16
-
-	stq $25,56($17)
-	addq $17,64,$17
-	addq $18,64,$18
-	bgt $16,2b
-
-	ret
-	.end xor_alpha_2
-
-	.align 3
-	.ent xor_alpha_3
-xor_alpha_3:
-	.prologue 0
-	srl $16, 6, $16
-	.align 4
-3:
-	ldq $0,0($17)
-	ldq $1,0($18)
-	ldq $2,0($19)
-	ldq $3,8($17)
-
-	ldq $4,8($18)
-	ldq $6,16($17)
-	ldq $7,16($18)
-	ldq $21,24($17)
-
-	ldq $22,24($18)
-	ldq $24,32($17)
-	ldq $25,32($18)
-	ldq $5,8($19)
-
-	ldq $20,16($19)
-	ldq $23,24($19)
-	ldq $27,32($19)
-	nop
-
-	xor $0,$1,$1		# 8 cycles from $0 load
-	xor $3,$4,$4		# 6 cycles from $4 load
-	xor $6,$7,$7		# 6 cycles from $7 load
-	xor $21,$22,$22		# 5 cycles from $22 load
-
-	xor $1,$2,$2		# 9 cycles from $2 load
-	xor $24,$25,$25		# 5 cycles from $25 load
-	stq $2,0($17)
-	xor $4,$5,$5		# 6 cycles from $5 load
-
-	stq $5,8($17)
-	xor $7,$20,$20		# 7 cycles from $20 load
-	stq $20,16($17)
-	xor $22,$23,$23		# 7 cycles from $23 load
-
-	stq $23,24($17)
-	xor $25,$27,$27		# 7 cycles from $27 load
-	stq $27,32($17)
-	nop
-
-	ldq $0,40($17)
-	ldq $1,40($18)
-	ldq $3,48($17)
-	ldq $4,48($18)
-
-	ldq $6,56($17)
-	ldq $7,56($18)
-	ldq $2,40($19)
-	ldq $5,48($19)
-
-	ldq $20,56($19)
-	xor $0,$1,$1		# 4 cycles from $1 load
-	xor $3,$4,$4		# 5 cycles from $4 load
-	xor $6,$7,$7		# 5 cycles from $7 load
-
-	xor $1,$2,$2		# 4 cycles from $2 load
-	xor $4,$5,$5		# 5 cycles from $5 load
-	stq $2,40($17)
-	xor $7,$20,$20		# 4 cycles from $20 load
-
-	stq $5,48($17)
-	subq $16,1,$16
-	stq $20,56($17)
-	addq $19,64,$19
-
-	addq $18,64,$18
-	addq $17,64,$17
-	bgt $16,3b
-	ret
-	.end xor_alpha_3
-
-	.align 3
-	.ent xor_alpha_4
-xor_alpha_4:
-	.prologue 0
-	srl $16, 6, $16
-	.align 4
-4:
-	ldq $0,0($17)
-	ldq $1,0($18)
-	ldq $2,0($19)
-	ldq $3,0($20)
-
-	ldq $4,8($17)
-	ldq $5,8($18)
-	ldq $6,8($19)
-	ldq $7,8($20)
-
-	ldq $21,16($17)
-	ldq $22,16($18)
-	ldq $23,16($19)
-	ldq $24,16($20)
-
-	ldq $25,24($17)
-	xor $0,$1,$1		# 6 cycles from $1 load
-	ldq $27,24($18)
-	xor $2,$3,$3		# 6 cycles from $3 load
-
-	ldq $0,24($19)
-	xor $1,$3,$3
-	ldq $1,24($20)
-	xor $4,$5,$5		# 7 cycles from $5 load
-
-	stq $3,0($17)
-	xor $6,$7,$7
-	xor $21,$22,$22		# 7 cycles from $22 load
-	xor $5,$7,$7
-
-	stq $7,8($17)
-	xor $23,$24,$24		# 7 cycles from $24 load
-	ldq $2,32($17)
-	xor $22,$24,$24
-
-	ldq $3,32($18)
-	ldq $4,32($19)
-	ldq $5,32($20)
-	xor $25,$27,$27		# 8 cycles from $27 load
-
-	ldq $6,40($17)
-	ldq $7,40($18)
-	ldq $21,40($19)
-	ldq $22,40($20)
-
-	stq $24,16($17)
-	xor $0,$1,$1		# 9 cycles from $1 load
-	xor $2,$3,$3		# 5 cycles from $3 load
-	xor $27,$1,$1
-
-	stq $1,24($17)
-	xor $4,$5,$5		# 5 cycles from $5 load
-	ldq $23,48($17)
-	ldq $24,48($18)
-
-	ldq $25,48($19)
-	xor $3,$5,$5
-	ldq $27,48($20)
-	ldq $0,56($17)
-
-	ldq $1,56($18)
-	ldq $2,56($19)
-	xor $6,$7,$7		# 8 cycles from $6 load
-	ldq $3,56($20)
-
-	stq $5,32($17)
-	xor $21,$22,$22		# 8 cycles from $22 load
-	xor $7,$22,$22
-	xor $23,$24,$24		# 5 cycles from $24 load
-
-	stq $22,40($17)
-	xor $25,$27,$27		# 5 cycles from $27 load
-	xor $24,$27,$27
-	xor $0,$1,$1		# 5 cycles from $1 load
-
-	stq $27,48($17)
-	xor $2,$3,$3		# 4 cycles from $3 load
-	xor $1,$3,$3
-	subq $16,1,$16
-
-	stq $3,56($17)
-	addq $20,64,$20
-	addq $19,64,$19
-	addq $18,64,$18
-
-	addq $17,64,$17
-	bgt $16,4b
-	ret
-	.end xor_alpha_4
-
-	.align 3
-	.ent xor_alpha_5
-xor_alpha_5:
-	.prologue 0
-	srl $16, 6, $16
-	.align 4
-5:
-	ldq $0,0($17)
-	ldq $1,0($18)
-	ldq $2,0($19)
-	ldq $3,0($20)
-
-	ldq $4,0($21)
-	ldq $5,8($17)
-	ldq $6,8($18)
-	ldq $7,8($19)
-
-	ldq $22,8($20)
-	ldq $23,8($21)
-	ldq $24,16($17)
-	ldq $25,16($18)
-
-	ldq $27,16($19)
-	xor $0,$1,$1		# 6 cycles from $1 load
-	ldq $28,16($20)
-	xor $2,$3,$3		# 6 cycles from $3 load
-
-	ldq $0,16($21)
-	xor $1,$3,$3
-	ldq $1,24($17)
-	xor $3,$4,$4		# 7 cycles from $4 load
-
-	stq $4,0($17)
-	xor $5,$6,$6		# 7 cycles from $6 load
-	xor $7,$22,$22		# 7 cycles from $22 load
-	xor $6,$23,$23		# 7 cycles from $23 load
-
-	ldq $2,24($18)
-	xor $22,$23,$23
-	ldq $3,24($19)
-	xor $24,$25,$25		# 8 cycles from $25 load
-
-	stq $23,8($17)
-	xor $25,$27,$27		# 8 cycles from $27 load
-	ldq $4,24($20)
-	xor $28,$0,$0		# 7 cycles from $0 load
-
-	ldq $5,24($21)
-	xor $27,$0,$0
-	ldq $6,32($17)
-	ldq $7,32($18)
-
-	stq $0,16($17)
-	xor $1,$2,$2		# 6 cycles from $2 load
-	ldq $22,32($19)
-	xor $3,$4,$4		# 4 cycles from $4 load
-	
-	ldq $23,32($20)
-	xor $2,$4,$4
-	ldq $24,32($21)
-	ldq $25,40($17)
-
-	ldq $27,40($18)
-	ldq $28,40($19)
-	ldq $0,40($20)
-	xor $4,$5,$5		# 7 cycles from $5 load
-
-	stq $5,24($17)
-	xor $6,$7,$7		# 7 cycles from $7 load
-	ldq $1,40($21)
-	ldq $2,48($17)
-
-	ldq $3,48($18)
-	xor $7,$22,$22		# 7 cycles from $22 load
-	ldq $4,48($19)
-	xor $23,$24,$24		# 6 cycles from $24 load
-
-	ldq $5,48($20)
-	xor $22,$24,$24
-	ldq $6,48($21)
-	xor $25,$27,$27		# 7 cycles from $27 load
-
-	stq $24,32($17)
-	xor $27,$28,$28		# 8 cycles from $28 load
-	ldq $7,56($17)
-	xor $0,$1,$1		# 6 cycles from $1 load
-
-	ldq $22,56($18)
-	ldq $23,56($19)
-	ldq $24,56($20)
-	ldq $25,56($21)
-
-	xor $28,$1,$1
-	xor $2,$3,$3		# 9 cycles from $3 load
-	xor $3,$4,$4		# 9 cycles from $4 load
-	xor $5,$6,$6		# 8 cycles from $6 load
-
-	stq $1,40($17)
-	xor $4,$6,$6
-	xor $7,$22,$22		# 7 cycles from $22 load
-	xor $23,$24,$24		# 6 cycles from $24 load
-
-	stq $6,48($17)
-	xor $22,$24,$24
-	subq $16,1,$16
-	xor $24,$25,$25		# 8 cycles from $25 load
-
-	stq $25,56($17)
-	addq $21,64,$21
-	addq $20,64,$20
-	addq $19,64,$19
-
-	addq $18,64,$18
-	addq $17,64,$17
-	bgt $16,5b
-	ret
-	.end xor_alpha_5
-
-	.align 3
-	.ent xor_alpha_prefetch_2
-xor_alpha_prefetch_2:
-	.prologue 0
-	srl $16, 6, $16
-
-	ldq $31, 0($17)
-	ldq $31, 0($18)
-
-	ldq $31, 64($17)
-	ldq $31, 64($18)
-
-	ldq $31, 128($17)
-	ldq $31, 128($18)
-
-	ldq $31, 192($17)
-	ldq $31, 192($18)
-	.align 4
-2:
-	ldq $0,0($17)
-	ldq $1,0($18)
-	ldq $2,8($17)
-	ldq $3,8($18)
-
-	ldq $4,16($17)
-	ldq $5,16($18)
-	ldq $6,24($17)
-	ldq $7,24($18)
-
-	ldq $19,32($17)
-	ldq $20,32($18)
-	ldq $21,40($17)
-	ldq $22,40($18)
-
-	ldq $23,48($17)
-	ldq $24,48($18)
-	ldq $25,56($17)
-	ldq $27,56($18)
-
-	ldq $31,256($17)
-	xor $0,$1,$0		# 8 cycles from $1 load
-	ldq $31,256($18)
-	xor $2,$3,$2
-
-	stq $0,0($17)
-	xor $4,$5,$4
-	stq $2,8($17)
-	xor $6,$7,$6
-
-	stq $4,16($17)
-	xor $19,$20,$19
-	stq $6,24($17)
-	xor $21,$22,$21
-
-	stq $19,32($17)
-	xor $23,$24,$23
-	stq $21,40($17)
-	xor $25,$27,$25
-
-	stq $23,48($17)
-	subq $16,1,$16
-	stq $25,56($17)
-	addq $17,64,$17
-
-	addq $18,64,$18
-	bgt $16,2b
-	ret
-	.end xor_alpha_prefetch_2
-
-	.align 3
-	.ent xor_alpha_prefetch_3
-xor_alpha_prefetch_3:
-	.prologue 0
-	srl $16, 6, $16
-
-	ldq $31, 0($17)
-	ldq $31, 0($18)
-	ldq $31, 0($19)
-
-	ldq $31, 64($17)
-	ldq $31, 64($18)
-	ldq $31, 64($19)
-
-	ldq $31, 128($17)
-	ldq $31, 128($18)
-	ldq $31, 128($19)
-
-	ldq $31, 192($17)
-	ldq $31, 192($18)
-	ldq $31, 192($19)
-	.align 4
-3:
-	ldq $0,0($17)
-	ldq $1,0($18)
-	ldq $2,0($19)
-	ldq $3,8($17)
-
-	ldq $4,8($18)
-	ldq $6,16($17)
-	ldq $7,16($18)
-	ldq $21,24($17)
-
-	ldq $22,24($18)
-	ldq $24,32($17)
-	ldq $25,32($18)
-	ldq $5,8($19)
-
-	ldq $20,16($19)
-	ldq $23,24($19)
-	ldq $27,32($19)
-	nop
-
-	xor $0,$1,$1		# 8 cycles from $0 load
-	xor $3,$4,$4		# 7 cycles from $4 load
-	xor $6,$7,$7		# 6 cycles from $7 load
-	xor $21,$22,$22		# 5 cycles from $22 load
-
-	xor $1,$2,$2		# 9 cycles from $2 load
-	xor $24,$25,$25		# 5 cycles from $25 load
-	stq $2,0($17)
-	xor $4,$5,$5		# 6 cycles from $5 load
-
-	stq $5,8($17)
-	xor $7,$20,$20		# 7 cycles from $20 load
-	stq $20,16($17)
-	xor $22,$23,$23		# 7 cycles from $23 load
-
-	stq $23,24($17)
-	xor $25,$27,$27		# 7 cycles from $27 load
-	stq $27,32($17)
-	nop
-
-	ldq $0,40($17)
-	ldq $1,40($18)
-	ldq $3,48($17)
-	ldq $4,48($18)
-
-	ldq $6,56($17)
-	ldq $7,56($18)
-	ldq $2,40($19)
-	ldq $5,48($19)
-
-	ldq $20,56($19)
-	ldq $31,256($17)
-	ldq $31,256($18)
-	ldq $31,256($19)
-
-	xor $0,$1,$1		# 6 cycles from $1 load
-	xor $3,$4,$4		# 5 cycles from $4 load
-	xor $6,$7,$7		# 5 cycles from $7 load
-	xor $1,$2,$2		# 4 cycles from $2 load
-	
-	xor $4,$5,$5		# 5 cycles from $5 load
-	xor $7,$20,$20		# 4 cycles from $20 load
-	stq $2,40($17)
-	subq $16,1,$16
-
-	stq $5,48($17)
-	addq $19,64,$19
-	stq $20,56($17)
-	addq $18,64,$18
-
-	addq $17,64,$17
-	bgt $16,3b
-	ret
-	.end xor_alpha_prefetch_3
-
-	.align 3
-	.ent xor_alpha_prefetch_4
-xor_alpha_prefetch_4:
-	.prologue 0
-	srl $16, 6, $16
-
-	ldq $31, 0($17)
-	ldq $31, 0($18)
-	ldq $31, 0($19)
-	ldq $31, 0($20)
-
-	ldq $31, 64($17)
-	ldq $31, 64($18)
-	ldq $31, 64($19)
-	ldq $31, 64($20)
-
-	ldq $31, 128($17)
-	ldq $31, 128($18)
-	ldq $31, 128($19)
-	ldq $31, 128($20)
-
-	ldq $31, 192($17)
-	ldq $31, 192($18)
-	ldq $31, 192($19)
-	ldq $31, 192($20)
-	.align 4
-4:
-	ldq $0,0($17)
-	ldq $1,0($18)
-	ldq $2,0($19)
-	ldq $3,0($20)
-
-	ldq $4,8($17)
-	ldq $5,8($18)
-	ldq $6,8($19)
-	ldq $7,8($20)
-
-	ldq $21,16($17)
-	ldq $22,16($18)
-	ldq $23,16($19)
-	ldq $24,16($20)
-
-	ldq $25,24($17)
-	xor $0,$1,$1		# 6 cycles from $1 load
-	ldq $27,24($18)
-	xor $2,$3,$3		# 6 cycles from $3 load
-
-	ldq $0,24($19)
-	xor $1,$3,$3
-	ldq $1,24($20)
-	xor $4,$5,$5		# 7 cycles from $5 load
-
-	stq $3,0($17)
-	xor $6,$7,$7
-	xor $21,$22,$22		# 7 cycles from $22 load
-	xor $5,$7,$7
-
-	stq $7,8($17)
-	xor $23,$24,$24		# 7 cycles from $24 load
-	ldq $2,32($17)
-	xor $22,$24,$24
-
-	ldq $3,32($18)
-	ldq $4,32($19)
-	ldq $5,32($20)
-	xor $25,$27,$27		# 8 cycles from $27 load
-
-	ldq $6,40($17)
-	ldq $7,40($18)
-	ldq $21,40($19)
-	ldq $22,40($20)
-
-	stq $24,16($17)
-	xor $0,$1,$1		# 9 cycles from $1 load
-	xor $2,$3,$3		# 5 cycles from $3 load
-	xor $27,$1,$1
-
-	stq $1,24($17)
-	xor $4,$5,$5		# 5 cycles from $5 load
-	ldq $23,48($17)
-	xor $3,$5,$5
-
-	ldq $24,48($18)
-	ldq $25,48($19)
-	ldq $27,48($20)
-	ldq $0,56($17)
-
-	ldq $1,56($18)
-	ldq $2,56($19)
-	ldq $3,56($20)
-	xor $6,$7,$7		# 8 cycles from $6 load
-
-	ldq $31,256($17)
-	xor $21,$22,$22		# 8 cycles from $22 load
-	ldq $31,256($18)
-	xor $7,$22,$22
-
-	ldq $31,256($19)
-	xor $23,$24,$24		# 6 cycles from $24 load
-	ldq $31,256($20)
-	xor $25,$27,$27		# 6 cycles from $27 load
-
-	stq $5,32($17)
-	xor $24,$27,$27
-	xor $0,$1,$1		# 7 cycles from $1 load
-	xor $2,$3,$3		# 6 cycles from $3 load
-
-	stq $22,40($17)
-	xor $1,$3,$3
-	stq $27,48($17)
-	subq $16,1,$16
-
-	stq $3,56($17)
-	addq $20,64,$20
-	addq $19,64,$19
-	addq $18,64,$18
-
-	addq $17,64,$17
-	bgt $16,4b
-	ret
-	.end xor_alpha_prefetch_4
-
-	.align 3
-	.ent xor_alpha_prefetch_5
-xor_alpha_prefetch_5:
-	.prologue 0
-	srl $16, 6, $16
-
-	ldq $31, 0($17)
-	ldq $31, 0($18)
-	ldq $31, 0($19)
-	ldq $31, 0($20)
-	ldq $31, 0($21)
-
-	ldq $31, 64($17)
-	ldq $31, 64($18)
-	ldq $31, 64($19)
-	ldq $31, 64($20)
-	ldq $31, 64($21)
-
-	ldq $31, 128($17)
-	ldq $31, 128($18)
-	ldq $31, 128($19)
-	ldq $31, 128($20)
-	ldq $31, 128($21)
-
-	ldq $31, 192($17)
-	ldq $31, 192($18)
-	ldq $31, 192($19)
-	ldq $31, 192($20)
-	ldq $31, 192($21)
-	.align 4
-5:
-	ldq $0,0($17)
-	ldq $1,0($18)
-	ldq $2,0($19)
-	ldq $3,0($20)
-
-	ldq $4,0($21)
-	ldq $5,8($17)
-	ldq $6,8($18)
-	ldq $7,8($19)
-
-	ldq $22,8($20)
-	ldq $23,8($21)
-	ldq $24,16($17)
-	ldq $25,16($18)
-
-	ldq $27,16($19)
-	xor $0,$1,$1		# 6 cycles from $1 load
-	ldq $28,16($20)
-	xor $2,$3,$3		# 6 cycles from $3 load
-
-	ldq $0,16($21)
-	xor $1,$3,$3
-	ldq $1,24($17)
-	xor $3,$4,$4		# 7 cycles from $4 load
-
-	stq $4,0($17)
-	xor $5,$6,$6		# 7 cycles from $6 load
-	xor $7,$22,$22		# 7 cycles from $22 load
-	xor $6,$23,$23		# 7 cycles from $23 load
-
-	ldq $2,24($18)
-	xor $22,$23,$23
-	ldq $3,24($19)
-	xor $24,$25,$25		# 8 cycles from $25 load
-
-	stq $23,8($17)
-	xor $25,$27,$27		# 8 cycles from $27 load
-	ldq $4,24($20)
-	xor $28,$0,$0		# 7 cycles from $0 load
-
-	ldq $5,24($21)
-	xor $27,$0,$0
-	ldq $6,32($17)
-	ldq $7,32($18)
-
-	stq $0,16($17)
-	xor $1,$2,$2		# 6 cycles from $2 load
-	ldq $22,32($19)
-	xor $3,$4,$4		# 4 cycles from $4 load
-	
-	ldq $23,32($20)
-	xor $2,$4,$4
-	ldq $24,32($21)
-	ldq $25,40($17)
-
-	ldq $27,40($18)
-	ldq $28,40($19)
-	ldq $0,40($20)
-	xor $4,$5,$5		# 7 cycles from $5 load
-
-	stq $5,24($17)
-	xor $6,$7,$7		# 7 cycles from $7 load
-	ldq $1,40($21)
-	ldq $2,48($17)
-
-	ldq $3,48($18)
-	xor $7,$22,$22		# 7 cycles from $22 load
-	ldq $4,48($19)
-	xor $23,$24,$24		# 6 cycles from $24 load
-
-	ldq $5,48($20)
-	xor $22,$24,$24
-	ldq $6,48($21)
-	xor $25,$27,$27		# 7 cycles from $27 load
-
-	stq $24,32($17)
-	xor $27,$28,$28		# 8 cycles from $28 load
-	ldq $7,56($17)
-	xor $0,$1,$1		# 6 cycles from $1 load
-
-	ldq $22,56($18)
-	ldq $23,56($19)
-	ldq $24,56($20)
-	ldq $25,56($21)
-
-	ldq $31,256($17)
-	xor $28,$1,$1
-	ldq $31,256($18)
-	xor $2,$3,$3		# 9 cycles from $3 load
-
-	ldq $31,256($19)
-	xor $3,$4,$4		# 9 cycles from $4 load
-	ldq $31,256($20)
-	xor $5,$6,$6		# 8 cycles from $6 load
-
-	stq $1,40($17)
-	xor $4,$6,$6
-	xor $7,$22,$22		# 7 cycles from $22 load
-	xor $23,$24,$24		# 6 cycles from $24 load
-
-	stq $6,48($17)
-	xor $22,$24,$24
-	ldq $31,256($21)
-	xor $24,$25,$25		# 8 cycles from $25 load
-
-	stq $25,56($17)
-	subq $16,1,$16
-	addq $21,64,$21
-	addq $20,64,$20
-
-	addq $19,64,$19
-	addq $18,64,$18
-	addq $17,64,$17
-	bgt $16,5b
-
-	ret
-	.end xor_alpha_prefetch_5
+asm("								\n\
+	.text							\n\
+	.align 3						\n\
+	.ent xor_alpha_2					\n\
+xor_alpha_2:							\n\
+	.prologue 0						\n\
+	srl $16, 6, $16						\n\
+	.align 4						\n\
+2:								\n\
+	ldq $0,0($17)						\n\
+	ldq $1,0($18)						\n\
+	ldq $2,8($17)						\n\
+	ldq $3,8($18)						\n\
+								\n\
+	ldq $4,16($17)						\n\
+	ldq $5,16($18)						\n\
+	ldq $6,24($17)						\n\
+	ldq $7,24($18)						\n\
+								\n\
+	ldq $19,32($17)						\n\
+	ldq $20,32($18)						\n\
+	ldq $21,40($17)						\n\
+	ldq $22,40($18)						\n\
+								\n\
+	ldq $23,48($17)						\n\
+	ldq $24,48($18)						\n\
+	ldq $25,56($17)						\n\
+	xor $0,$1,$0		# 7 cycles from $1 load		\n\
+								\n\
+	ldq $27,56($18)						\n\
+	xor $2,$3,$2						\n\
+	stq $0,0($17)						\n\
+	xor $4,$5,$4						\n\
+								\n\
+	stq $2,8($17)						\n\
+	xor $6,$7,$6						\n\
+	stq $4,16($17)						\n\
+	xor $19,$20,$19						\n\
+								\n\
+	stq $6,24($17)						\n\
+	xor $21,$22,$21						\n\
+	stq $19,32($17)						\n\
+	xor $23,$24,$23						\n\
+								\n\
+	stq $21,40($17)						\n\
+	xor $25,$27,$25						\n\
+	stq $23,48($17)						\n\
+	subq $16,1,$16						\n\
+								\n\
+	stq $25,56($17)						\n\
+	addq $17,64,$17						\n\
+	addq $18,64,$18						\n\
+	bgt $16,2b						\n\
+								\n\
+	ret							\n\
+	.end xor_alpha_2					\n\
+								\n\
+	.align 3						\n\
+	.ent xor_alpha_3					\n\
+xor_alpha_3:							\n\
+	.prologue 0						\n\
+	srl $16, 6, $16						\n\
+	.align 4						\n\
+3:								\n\
+	ldq $0,0($17)						\n\
+	ldq $1,0($18)						\n\
+	ldq $2,0($19)						\n\
+	ldq $3,8($17)						\n\
+								\n\
+	ldq $4,8($18)						\n\
+	ldq $6,16($17)						\n\
+	ldq $7,16($18)						\n\
+	ldq $21,24($17)						\n\
+								\n\
+	ldq $22,24($18)						\n\
+	ldq $24,32($17)						\n\
+	ldq $25,32($18)						\n\
+	ldq $5,8($19)						\n\
+								\n\
+	ldq $20,16($19)						\n\
+	ldq $23,24($19)						\n\
+	ldq $27,32($19)						\n\
+	nop							\n\
+								\n\
+	xor $0,$1,$1		# 8 cycles from $0 load		\n\
+	xor $3,$4,$4		# 6 cycles from $4 load		\n\
+	xor $6,$7,$7		# 6 cycles from $7 load		\n\
+	xor $21,$22,$22		# 5 cycles from $22 load	\n\
+								\n\
+	xor $1,$2,$2		# 9 cycles from $2 load		\n\
+	xor $24,$25,$25		# 5 cycles from $25 load	\n\
+	stq $2,0($17)						\n\
+	xor $4,$5,$5		# 6 cycles from $5 load		\n\
+								\n\
+	stq $5,8($17)						\n\
+	xor $7,$20,$20		# 7 cycles from $20 load	\n\
+	stq $20,16($17)						\n\
+	xor $22,$23,$23		# 7 cycles from $23 load	\n\
+								\n\
+	stq $23,24($17)						\n\
+	xor $25,$27,$27		# 7 cycles from $27 load	\n\
+	stq $27,32($17)						\n\
+	nop							\n\
+								\n\
+	ldq $0,40($17)						\n\
+	ldq $1,40($18)						\n\
+	ldq $3,48($17)						\n\
+	ldq $4,48($18)						\n\
+								\n\
+	ldq $6,56($17)						\n\
+	ldq $7,56($18)						\n\
+	ldq $2,40($19)						\n\
+	ldq $5,48($19)						\n\
+								\n\
+	ldq $20,56($19)						\n\
+	xor $0,$1,$1		# 4 cycles from $1 load		\n\
+	xor $3,$4,$4		# 5 cycles from $4 load		\n\
+	xor $6,$7,$7		# 5 cycles from $7 load		\n\
+								\n\
+	xor $1,$2,$2		# 4 cycles from $2 load		\n\
+	xor $4,$5,$5		# 5 cycles from $5 load		\n\
+	stq $2,40($17)						\n\
+	xor $7,$20,$20		# 4 cycles from $20 load	\n\
+								\n\
+	stq $5,48($17)						\n\
+	subq $16,1,$16						\n\
+	stq $20,56($17)						\n\
+	addq $19,64,$19						\n\
+								\n\
+	addq $18,64,$18						\n\
+	addq $17,64,$17						\n\
+	bgt $16,3b						\n\
+	ret							\n\
+	.end xor_alpha_3					\n\
+								\n\
+	.align 3						\n\
+	.ent xor_alpha_4					\n\
+xor_alpha_4:							\n\
+	.prologue 0						\n\
+	srl $16, 6, $16						\n\
+	.align 4						\n\
+4:								\n\
+	ldq $0,0($17)						\n\
+	ldq $1,0($18)						\n\
+	ldq $2,0($19)						\n\
+	ldq $3,0($20)						\n\
+								\n\
+	ldq $4,8($17)						\n\
+	ldq $5,8($18)						\n\
+	ldq $6,8($19)						\n\
+	ldq $7,8($20)						\n\
+								\n\
+	ldq $21,16($17)						\n\
+	ldq $22,16($18)						\n\
+	ldq $23,16($19)						\n\
+	ldq $24,16($20)						\n\
+								\n\
+	ldq $25,24($17)						\n\
+	xor $0,$1,$1		# 6 cycles from $1 load		\n\
+	ldq $27,24($18)						\n\
+	xor $2,$3,$3		# 6 cycles from $3 load		\n\
+								\n\
+	ldq $0,24($19)						\n\
+	xor $1,$3,$3						\n\
+	ldq $1,24($20)						\n\
+	xor $4,$5,$5		# 7 cycles from $5 load		\n\
+								\n\
+	stq $3,0($17)						\n\
+	xor $6,$7,$7						\n\
+	xor $21,$22,$22		# 7 cycles from $22 load	\n\
+	xor $5,$7,$7						\n\
+								\n\
+	stq $7,8($17)						\n\
+	xor $23,$24,$24		# 7 cycles from $24 load	\n\
+	ldq $2,32($17)						\n\
+	xor $22,$24,$24						\n\
+								\n\
+	ldq $3,32($18)						\n\
+	ldq $4,32($19)						\n\
+	ldq $5,32($20)						\n\
+	xor $25,$27,$27		# 8 cycles from $27 load	\n\
+								\n\
+	ldq $6,40($17)						\n\
+	ldq $7,40($18)						\n\
+	ldq $21,40($19)						\n\
+	ldq $22,40($20)						\n\
+								\n\
+	stq $24,16($17)						\n\
+	xor $0,$1,$1		# 9 cycles from $1 load		\n\
+	xor $2,$3,$3		# 5 cycles from $3 load		\n\
+	xor $27,$1,$1						\n\
+								\n\
+	stq $1,24($17)						\n\
+	xor $4,$5,$5		# 5 cycles from $5 load		\n\
+	ldq $23,48($17)						\n\
+	ldq $24,48($18)						\n\
+								\n\
+	ldq $25,48($19)						\n\
+	xor $3,$5,$5						\n\
+	ldq $27,48($20)						\n\
+	ldq $0,56($17)						\n\
+								\n\
+	ldq $1,56($18)						\n\
+	ldq $2,56($19)						\n\
+	xor $6,$7,$7		# 8 cycles from $6 load		\n\
+	ldq $3,56($20)						\n\
+								\n\
+	stq $5,32($17)						\n\
+	xor $21,$22,$22		# 8 cycles from $22 load	\n\
+	xor $7,$22,$22						\n\
+	xor $23,$24,$24		# 5 cycles from $24 load	\n\
+								\n\
+	stq $22,40($17)						\n\
+	xor $25,$27,$27		# 5 cycles from $27 load	\n\
+	xor $24,$27,$27						\n\
+	xor $0,$1,$1		# 5 cycles from $1 load		\n\
+								\n\
+	stq $27,48($17)						\n\
+	xor $2,$3,$3		# 4 cycles from $3 load		\n\
+	xor $1,$3,$3						\n\
+	subq $16,1,$16						\n\
+								\n\
+	stq $3,56($17)						\n\
+	addq $20,64,$20						\n\
+	addq $19,64,$19						\n\
+	addq $18,64,$18						\n\
+								\n\
+	addq $17,64,$17						\n\
+	bgt $16,4b						\n\
+	ret							\n\
+	.end xor_alpha_4					\n\
+								\n\
+	.align 3						\n\
+	.ent xor_alpha_5					\n\
+xor_alpha_5:							\n\
+	.prologue 0						\n\
+	srl $16, 6, $16						\n\
+	.align 4						\n\
+5:								\n\
+	ldq $0,0($17)						\n\
+	ldq $1,0($18)						\n\
+	ldq $2,0($19)						\n\
+	ldq $3,0($20)						\n\
+								\n\
+	ldq $4,0($21)						\n\
+	ldq $5,8($17)						\n\
+	ldq $6,8($18)						\n\
+	ldq $7,8($19)						\n\
+								\n\
+	ldq $22,8($20)						\n\
+	ldq $23,8($21)						\n\
+	ldq $24,16($17)						\n\
+	ldq $25,16($18)						\n\
+								\n\
+	ldq $27,16($19)						\n\
+	xor $0,$1,$1		# 6 cycles from $1 load		\n\
+	ldq $28,16($20)						\n\
+	xor $2,$3,$3		# 6 cycles from $3 load		\n\
+								\n\
+	ldq $0,16($21)						\n\
+	xor $1,$3,$3						\n\
+	ldq $1,24($17)						\n\
+	xor $3,$4,$4		# 7 cycles from $4 load		\n\
+								\n\
+	stq $4,0($17)						\n\
+	xor $5,$6,$6		# 7 cycles from $6 load		\n\
+	xor $7,$22,$22		# 7 cycles from $22 load	\n\
+	xor $6,$23,$23		# 7 cycles from $23 load	\n\
+								\n\
+	ldq $2,24($18)						\n\
+	xor $22,$23,$23						\n\
+	ldq $3,24($19)						\n\
+	xor $24,$25,$25		# 8 cycles from $25 load	\n\
+								\n\
+	stq $23,8($17)						\n\
+	xor $25,$27,$27		# 8 cycles from $27 load	\n\
+	ldq $4,24($20)						\n\
+	xor $28,$0,$0		# 7 cycles from $0 load		\n\
+								\n\
+	ldq $5,24($21)						\n\
+	xor $27,$0,$0						\n\
+	ldq $6,32($17)						\n\
+	ldq $7,32($18)						\n\
+								\n\
+	stq $0,16($17)						\n\
+	xor $1,$2,$2		# 6 cycles from $2 load		\n\
+	ldq $22,32($19)						\n\
+	xor $3,$4,$4		# 4 cycles from $4 load		\n\
+								\n\
+	ldq $23,32($20)						\n\
+	xor $2,$4,$4						\n\
+	ldq $24,32($21)						\n\
+	ldq $25,40($17)						\n\
+								\n\
+	ldq $27,40($18)						\n\
+	ldq $28,40($19)						\n\
+	ldq $0,40($20)						\n\
+	xor $4,$5,$5		# 7 cycles from $5 load		\n\
+								\n\
+	stq $5,24($17)						\n\
+	xor $6,$7,$7		# 7 cycles from $7 load		\n\
+	ldq $1,40($21)						\n\
+	ldq $2,48($17)						\n\
+								\n\
+	ldq $3,48($18)						\n\
+	xor $7,$22,$22		# 7 cycles from $22 load	\n\
+	ldq $4,48($19)						\n\
+	xor $23,$24,$24		# 6 cycles from $24 load	\n\
+								\n\
+	ldq $5,48($20)						\n\
+	xor $22,$24,$24						\n\
+	ldq $6,48($21)						\n\
+	xor $25,$27,$27		# 7 cycles from $27 load	\n\
+								\n\
+	stq $24,32($17)						\n\
+	xor $27,$28,$28		# 8 cycles from $28 load	\n\
+	ldq $7,56($17)						\n\
+	xor $0,$1,$1		# 6 cycles from $1 load		\n\
+								\n\
+	ldq $22,56($18)						\n\
+	ldq $23,56($19)						\n\
+	ldq $24,56($20)						\n\
+	ldq $25,56($21)						\n\
+								\n\
+	xor $28,$1,$1						\n\
+	xor $2,$3,$3		# 9 cycles from $3 load		\n\
+	xor $3,$4,$4		# 9 cycles from $4 load		\n\
+	xor $5,$6,$6		# 8 cycles from $6 load		\n\
+								\n\
+	stq $1,40($17)						\n\
+	xor $4,$6,$6						\n\
+	xor $7,$22,$22		# 7 cycles from $22 load	\n\
+	xor $23,$24,$24		# 6 cycles from $24 load	\n\
+								\n\
+	stq $6,48($17)						\n\
+	xor $22,$24,$24						\n\
+	subq $16,1,$16						\n\
+	xor $24,$25,$25		# 8 cycles from $25 load	\n\
+								\n\
+	stq $25,56($17)						\n\
+	addq $21,64,$21						\n\
+	addq $20,64,$20						\n\
+	addq $19,64,$19						\n\
+								\n\
+	addq $18,64,$18						\n\
+	addq $17,64,$17						\n\
+	bgt $16,5b						\n\
+	ret							\n\
+	.end xor_alpha_5					\n\
+								\n\
+	.align 3						\n\
+	.ent xor_alpha_prefetch_2				\n\
+xor_alpha_prefetch_2:						\n\
+	.prologue 0						\n\
+	srl $16, 6, $16						\n\
+								\n\
+	ldq $31, 0($17)						\n\
+	ldq $31, 0($18)						\n\
+								\n\
+	ldq $31, 64($17)					\n\
+	ldq $31, 64($18)					\n\
+								\n\
+	ldq $31, 128($17)					\n\
+	ldq $31, 128($18)					\n\
+								\n\
+	ldq $31, 192($17)					\n\
+	ldq $31, 192($18)					\n\
+	.align 4						\n\
+2:								\n\
+	ldq $0,0($17)						\n\
+	ldq $1,0($18)						\n\
+	ldq $2,8($17)						\n\
+	ldq $3,8($18)						\n\
+								\n\
+	ldq $4,16($17)						\n\
+	ldq $5,16($18)						\n\
+	ldq $6,24($17)						\n\
+	ldq $7,24($18)						\n\
+								\n\
+	ldq $19,32($17)						\n\
+	ldq $20,32($18)						\n\
+	ldq $21,40($17)						\n\
+	ldq $22,40($18)						\n\
+								\n\
+	ldq $23,48($17)						\n\
+	ldq $24,48($18)						\n\
+	ldq $25,56($17)						\n\
+	ldq $27,56($18)						\n\
+								\n\
+	ldq $31,256($17)					\n\
+	xor $0,$1,$0		# 8 cycles from $1 load		\n\
+	ldq $31,256($18)					\n\
+	xor $2,$3,$2						\n\
+								\n\
+	stq $0,0($17)						\n\
+	xor $4,$5,$4						\n\
+	stq $2,8($17)						\n\
+	xor $6,$7,$6						\n\
+								\n\
+	stq $4,16($17)						\n\
+	xor $19,$20,$19						\n\
+	stq $6,24($17)						\n\
+	xor $21,$22,$21						\n\
+								\n\
+	stq $19,32($17)						\n\
+	xor $23,$24,$23						\n\
+	stq $21,40($17)						\n\
+	xor $25,$27,$25						\n\
+								\n\
+	stq $23,48($17)						\n\
+	subq $16,1,$16						\n\
+	stq $25,56($17)						\n\
+	addq $17,64,$17						\n\
+								\n\
+	addq $18,64,$18						\n\
+	bgt $16,2b						\n\
+	ret							\n\
+	.end xor_alpha_prefetch_2				\n\
+								\n\
+	.align 3						\n\
+	.ent xor_alpha_prefetch_3				\n\
+xor_alpha_prefetch_3:						\n\
+	.prologue 0						\n\
+	srl $16, 6, $16						\n\
+								\n\
+	ldq $31, 0($17)						\n\
+	ldq $31, 0($18)						\n\
+	ldq $31, 0($19)						\n\
+								\n\
+	ldq $31, 64($17)					\n\
+	ldq $31, 64($18)					\n\
+	ldq $31, 64($19)					\n\
+								\n\
+	ldq $31, 128($17)					\n\
+	ldq $31, 128($18)					\n\
+	ldq $31, 128($19)					\n\
+								\n\
+	ldq $31, 192($17)					\n\
+	ldq $31, 192($18)					\n\
+	ldq $31, 192($19)					\n\
+	.align 4						\n\
+3:								\n\
+	ldq $0,0($17)						\n\
+	ldq $1,0($18)						\n\
+	ldq $2,0($19)						\n\
+	ldq $3,8($17)						\n\
+								\n\
+	ldq $4,8($18)						\n\
+	ldq $6,16($17)						\n\
+	ldq $7,16($18)						\n\
+	ldq $21,24($17)						\n\
+								\n\
+	ldq $22,24($18)						\n\
+	ldq $24,32($17)						\n\
+	ldq $25,32($18)						\n\
+	ldq $5,8($19)						\n\
+								\n\
+	ldq $20,16($19)						\n\
+	ldq $23,24($19)						\n\
+	ldq $27,32($19)						\n\
+	nop							\n\
+								\n\
+	xor $0,$1,$1		# 8 cycles from $0 load		\n\
+	xor $3,$4,$4		# 7 cycles from $4 load		\n\
+	xor $6,$7,$7		# 6 cycles from $7 load		\n\
+	xor $21,$22,$22		# 5 cycles from $22 load	\n\
+								\n\
+	xor $1,$2,$2		# 9 cycles from $2 load		\n\
+	xor $24,$25,$25		# 5 cycles from $25 load	\n\
+	stq $2,0($17)						\n\
+	xor $4,$5,$5		# 6 cycles from $5 load		\n\
+								\n\
+	stq $5,8($17)						\n\
+	xor $7,$20,$20		# 7 cycles from $20 load	\n\
+	stq $20,16($17)						\n\
+	xor $22,$23,$23		# 7 cycles from $23 load	\n\
+								\n\
+	stq $23,24($17)						\n\
+	xor $25,$27,$27		# 7 cycles from $27 load	\n\
+	stq $27,32($17)						\n\
+	nop							\n\
+								\n\
+	ldq $0,40($17)						\n\
+	ldq $1,40($18)						\n\
+	ldq $3,48($17)						\n\
+	ldq $4,48($18)						\n\
+								\n\
+	ldq $6,56($17)						\n\
+	ldq $7,56($18)						\n\
+	ldq $2,40($19)						\n\
+	ldq $5,48($19)						\n\
+								\n\
+	ldq $20,56($19)						\n\
+	ldq $31,256($17)					\n\
+	ldq $31,256($18)					\n\
+	ldq $31,256($19)					\n\
+								\n\
+	xor $0,$1,$1		# 6 cycles from $1 load		\n\
+	xor $3,$4,$4		# 5 cycles from $4 load		\n\
+	xor $6,$7,$7		# 5 cycles from $7 load		\n\
+	xor $1,$2,$2		# 4 cycles from $2 load		\n\
+								\n\
+	xor $4,$5,$5		# 5 cycles from $5 load		\n\
+	xor $7,$20,$20		# 4 cycles from $20 load	\n\
+	stq $2,40($17)						\n\
+	subq $16,1,$16						\n\
+								\n\
+	stq $5,48($17)						\n\
+	addq $19,64,$19						\n\
+	stq $20,56($17)						\n\
+	addq $18,64,$18						\n\
+								\n\
+	addq $17,64,$17						\n\
+	bgt $16,3b						\n\
+	ret							\n\
+	.end xor_alpha_prefetch_3				\n\
+								\n\
+	.align 3						\n\
+	.ent xor_alpha_prefetch_4				\n\
+xor_alpha_prefetch_4:						\n\
+	.prologue 0						\n\
+	srl $16, 6, $16						\n\
+								\n\
+	ldq $31, 0($17)						\n\
+	ldq $31, 0($18)						\n\
+	ldq $31, 0($19)						\n\
+	ldq $31, 0($20)						\n\
+								\n\
+	ldq $31, 64($17)					\n\
+	ldq $31, 64($18)					\n\
+	ldq $31, 64($19)					\n\
+	ldq $31, 64($20)					\n\
+								\n\
+	ldq $31, 128($17)					\n\
+	ldq $31, 128($18)					\n\
+	ldq $31, 128($19)					\n\
+	ldq $31, 128($20)					\n\
+								\n\
+	ldq $31, 192($17)					\n\
+	ldq $31, 192($18)					\n\
+	ldq $31, 192($19)					\n\
+	ldq $31, 192($20)					\n\
+	.align 4						\n\
+4:								\n\
+	ldq $0,0($17)						\n\
+	ldq $1,0($18)						\n\
+	ldq $2,0($19)						\n\
+	ldq $3,0($20)						\n\
+								\n\
+	ldq $4,8($17)						\n\
+	ldq $5,8($18)						\n\
+	ldq $6,8($19)						\n\
+	ldq $7,8($20)						\n\
+								\n\
+	ldq $21,16($17)						\n\
+	ldq $22,16($18)						\n\
+	ldq $23,16($19)						\n\
+	ldq $24,16($20)						\n\
+								\n\
+	ldq $25,24($17)						\n\
+	xor $0,$1,$1		# 6 cycles from $1 load		\n\
+	ldq $27,24($18)						\n\
+	xor $2,$3,$3		# 6 cycles from $3 load		\n\
+								\n\
+	ldq $0,24($19)						\n\
+	xor $1,$3,$3						\n\
+	ldq $1,24($20)						\n\
+	xor $4,$5,$5		# 7 cycles from $5 load		\n\
+								\n\
+	stq $3,0($17)						\n\
+	xor $6,$7,$7						\n\
+	xor $21,$22,$22		# 7 cycles from $22 load	\n\
+	xor $5,$7,$7						\n\
+								\n\
+	stq $7,8($17)						\n\
+	xor $23,$24,$24		# 7 cycles from $24 load	\n\
+	ldq $2,32($17)						\n\
+	xor $22,$24,$24						\n\
+								\n\
+	ldq $3,32($18)						\n\
+	ldq $4,32($19)						\n\
+	ldq $5,32($20)						\n\
+	xor $25,$27,$27		# 8 cycles from $27 load	\n\
+								\n\
+	ldq $6,40($17)						\n\
+	ldq $7,40($18)						\n\
+	ldq $21,40($19)						\n\
+	ldq $22,40($20)						\n\
+								\n\
+	stq $24,16($17)						\n\
+	xor $0,$1,$1		# 9 cycles from $1 load		\n\
+	xor $2,$3,$3		# 5 cycles from $3 load		\n\
+	xor $27,$1,$1						\n\
+								\n\
+	stq $1,24($17)						\n\
+	xor $4,$5,$5		# 5 cycles from $5 load		\n\
+	ldq $23,48($17)						\n\
+	xor $3,$5,$5						\n\
+								\n\
+	ldq $24,48($18)						\n\
+	ldq $25,48($19)						\n\
+	ldq $27,48($20)						\n\
+	ldq $0,56($17)						\n\
+								\n\
+	ldq $1,56($18)						\n\
+	ldq $2,56($19)						\n\
+	ldq $3,56($20)						\n\
+	xor $6,$7,$7		# 8 cycles from $6 load		\n\
+								\n\
+	ldq $31,256($17)					\n\
+	xor $21,$22,$22		# 8 cycles from $22 load	\n\
+	ldq $31,256($18)					\n\
+	xor $7,$22,$22						\n\
+								\n\
+	ldq $31,256($19)					\n\
+	xor $23,$24,$24		# 6 cycles from $24 load	\n\
+	ldq $31,256($20)					\n\
+	xor $25,$27,$27		# 6 cycles from $27 load	\n\
+								\n\
+	stq $5,32($17)						\n\
+	xor $24,$27,$27						\n\
+	xor $0,$1,$1		# 7 cycles from $1 load		\n\
+	xor $2,$3,$3		# 6 cycles from $3 load		\n\
+								\n\
+	stq $22,40($17)						\n\
+	xor $1,$3,$3						\n\
+	stq $27,48($17)						\n\
+	subq $16,1,$16						\n\
+								\n\
+	stq $3,56($17)						\n\
+	addq $20,64,$20						\n\
+	addq $19,64,$19						\n\
+	addq $18,64,$18						\n\
+								\n\
+	addq $17,64,$17						\n\
+	bgt $16,4b						\n\
+	ret							\n\
+	.end xor_alpha_prefetch_4				\n\
+								\n\
+	.align 3						\n\
+	.ent xor_alpha_prefetch_5				\n\
+xor_alpha_prefetch_5:						\n\
+	.prologue 0						\n\
+	srl $16, 6, $16						\n\
+								\n\
+	ldq $31, 0($17)						\n\
+	ldq $31, 0($18)						\n\
+	ldq $31, 0($19)						\n\
+	ldq $31, 0($20)						\n\
+	ldq $31, 0($21)						\n\
+								\n\
+	ldq $31, 64($17)					\n\
+	ldq $31, 64($18)					\n\
+	ldq $31, 64($19)					\n\
+	ldq $31, 64($20)					\n\
+	ldq $31, 64($21)					\n\
+								\n\
+	ldq $31, 128($17)					\n\
+	ldq $31, 128($18)					\n\
+	ldq $31, 128($19)					\n\
+	ldq $31, 128($20)					\n\
+	ldq $31, 128($21)					\n\
+								\n\
+	ldq $31, 192($17)					\n\
+	ldq $31, 192($18)					\n\
+	ldq $31, 192($19)					\n\
+	ldq $31, 192($20)					\n\
+	ldq $31, 192($21)					\n\
+	.align 4						\n\
+5:								\n\
+	ldq $0,0($17)						\n\
+	ldq $1,0($18)						\n\
+	ldq $2,0($19)						\n\
+	ldq $3,0($20)						\n\
+								\n\
+	ldq $4,0($21)						\n\
+	ldq $5,8($17)						\n\
+	ldq $6,8($18)						\n\
+	ldq $7,8($19)						\n\
+								\n\
+	ldq $22,8($20)						\n\
+	ldq $23,8($21)						\n\
+	ldq $24,16($17)						\n\
+	ldq $25,16($18)						\n\
+								\n\
+	ldq $27,16($19)						\n\
+	xor $0,$1,$1		# 6 cycles from $1 load		\n\
+	ldq $28,16($20)						\n\
+	xor $2,$3,$3		# 6 cycles from $3 load		\n\
+								\n\
+	ldq $0,16($21)						\n\
+	xor $1,$3,$3						\n\
+	ldq $1,24($17)						\n\
+	xor $3,$4,$4		# 7 cycles from $4 load		\n\
+								\n\
+	stq $4,0($17)						\n\
+	xor $5,$6,$6		# 7 cycles from $6 load		\n\
+	xor $7,$22,$22		# 7 cycles from $22 load	\n\
+	xor $6,$23,$23		# 7 cycles from $23 load	\n\
+								\n\
+	ldq $2,24($18)						\n\
+	xor $22,$23,$23						\n\
+	ldq $3,24($19)						\n\
+	xor $24,$25,$25		# 8 cycles from $25 load	\n\
+								\n\
+	stq $23,8($17)						\n\
+	xor $25,$27,$27		# 8 cycles from $27 load	\n\
+	ldq $4,24($20)						\n\
+	xor $28,$0,$0		# 7 cycles from $0 load		\n\
+								\n\
+	ldq $5,24($21)						\n\
+	xor $27,$0,$0						\n\
+	ldq $6,32($17)						\n\
+	ldq $7,32($18)						\n\
+								\n\
+	stq $0,16($17)						\n\
+	xor $1,$2,$2		# 6 cycles from $2 load		\n\
+	ldq $22,32($19)						\n\
+	xor $3,$4,$4		# 4 cycles from $4 load		\n\
+								\n\
+	ldq $23,32($20)						\n\
+	xor $2,$4,$4						\n\
+	ldq $24,32($21)						\n\
+	ldq $25,40($17)						\n\
+								\n\
+	ldq $27,40($18)						\n\
+	ldq $28,40($19)						\n\
+	ldq $0,40($20)						\n\
+	xor $4,$5,$5		# 7 cycles from $5 load		\n\
+								\n\
+	stq $5,24($17)						\n\
+	xor $6,$7,$7		# 7 cycles from $7 load		\n\
+	ldq $1,40($21)						\n\
+	ldq $2,48($17)						\n\
+								\n\
+	ldq $3,48($18)						\n\
+	xor $7,$22,$22		# 7 cycles from $22 load	\n\
+	ldq $4,48($19)						\n\
+	xor $23,$24,$24		# 6 cycles from $24 load	\n\
+								\n\
+	ldq $5,48($20)						\n\
+	xor $22,$24,$24						\n\
+	ldq $6,48($21)						\n\
+	xor $25,$27,$27		# 7 cycles from $27 load	\n\
+								\n\
+	stq $24,32($17)						\n\
+	xor $27,$28,$28		# 8 cycles from $28 load	\n\
+	ldq $7,56($17)						\n\
+	xor $0,$1,$1		# 6 cycles from $1 load		\n\
+								\n\
+	ldq $22,56($18)						\n\
+	ldq $23,56($19)						\n\
+	ldq $24,56($20)						\n\
+	ldq $25,56($21)						\n\
+								\n\
+	ldq $31,256($17)					\n\
+	xor $28,$1,$1						\n\
+	ldq $31,256($18)					\n\
+	xor $2,$3,$3		# 9 cycles from $3 load		\n\
+								\n\
+	ldq $31,256($19)					\n\
+	xor $3,$4,$4		# 9 cycles from $4 load		\n\
+	ldq $31,256($20)					\n\
+	xor $5,$6,$6		# 8 cycles from $6 load		\n\
+								\n\
+	stq $1,40($17)						\n\
+	xor $4,$6,$6						\n\
+	xor $7,$22,$22		# 7 cycles from $22 load	\n\
+	xor $23,$24,$24		# 6 cycles from $24 load	\n\
+								\n\
+	stq $6,48($17)						\n\
+	xor $22,$24,$24						\n\
+	ldq $31,256($21)					\n\
+	xor $24,$25,$25		# 8 cycles from $25 load	\n\
+								\n\
+	stq $25,56($17)						\n\
+	subq $16,1,$16						\n\
+	addq $21,64,$21						\n\
+	addq $20,64,$20						\n\
+								\n\
+	addq $19,64,$19						\n\
+	addq $18,64,$18						\n\
+	addq $17,64,$17						\n\
+	bgt $16,5b						\n\
+								\n\
+	ret							\n\
+	.end xor_alpha_prefetch_5				\n\
 ");
 
 static struct xor_block_template xor_block_alpha = {
-	name: "alpha",
-	do_2: xor_alpha_2,
-	do_3: xor_alpha_3,
-	do_4: xor_alpha_4,
-	do_5: xor_alpha_5,
+	.name	= "alpha",
+	.do_2	= xor_alpha_2,
+	.do_3	= xor_alpha_3,
+	.do_4	= xor_alpha_4,
+	.do_5	= xor_alpha_5,
 };
 
 static struct xor_block_template xor_block_alpha_prefetch = {
-	name: "alpha prefetch",
-	do_2: xor_alpha_prefetch_2,
-	do_3: xor_alpha_prefetch_3,
-	do_4: xor_alpha_prefetch_4,
-	do_5: xor_alpha_prefetch_5,
+	.name	= "alpha prefetch",
+	.do_2	= xor_alpha_prefetch_2,
+	.do_3	= xor_alpha_prefetch_3,
+	.do_4	= xor_alpha_prefetch_4,
+	.do_5	= xor_alpha_prefetch_5,
 };
 
 /* For grins, also test the generic routines.  */
diff -urN linux-2.4.23-pre5/include/asm-arm/pci.h linux-2.4.23-pre6/include/asm-arm/pci.h
--- linux-2.4.23-pre5/include/asm-arm/pci.h	2003-08-25 04:44:43.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-arm/pci.h	2003-10-01 10:38:57.000000000 -0700
@@ -53,6 +53,8 @@
 	/* We don't do dynamic PCI IRQ allocation */
 }
 
+#define pcibios_scan_all_fns()		0
+
 struct pci_dev;
 
 /* Allocate and map kernel buffer using consistent mode DMA for a device.
diff -urN linux-2.4.23-pre5/include/asm-i386/pci.h linux-2.4.23-pre6/include/asm-i386/pci.h
--- linux-2.4.23-pre5/include/asm-i386/pci.h	2003-08-25 04:44:43.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-i386/pci.h	2003-10-01 10:38:57.000000000 -0700
@@ -14,6 +14,7 @@
 #else
 #define pcibios_assign_all_busses()	0
 #endif
+#define pcibios_scan_all_fns()		0
 
 extern unsigned long pci_mem_start;
 #define PCIBIOS_MIN_IO		0x1000
diff -urN linux-2.4.23-pre5/include/asm-ia64/pci.h linux-2.4.23-pre6/include/asm-ia64/pci.h
--- linux-2.4.23-pre5/include/asm-ia64/pci.h	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-ia64/pci.h	2003-10-01 10:38:57.000000000 -0700
@@ -15,6 +15,7 @@
  * loader.
  */
 #define pcibios_assign_all_busses()     0
+#define pcibios_scan_all_fns()		0
 
 #define PCIBIOS_MIN_IO		0x1000
 #define PCIBIOS_MIN_MEM		0x10000000
diff -urN linux-2.4.23-pre5/include/asm-m68k/pci.h linux-2.4.23-pre6/include/asm-m68k/pci.h
--- linux-2.4.23-pre5/include/asm-m68k/pci.h	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.23-pre6/include/asm-m68k/pci.h	2003-10-01 10:38:57.000000000 -0700
@@ -34,6 +34,7 @@
 };
 
 #define pcibios_assign_all_busses()	0
+#define pcibios_scan_all_fns()		0
 
 extern inline void pcibios_set_master(struct pci_dev *dev)
 {
diff -urN linux-2.4.23-pre5/include/asm-mips/pci.h linux-2.4.23-pre6/include/asm-mips/pci.h
--- linux-2.4.23-pre5/include/asm-mips/pci.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-mips/pci.h	2003-10-01 10:38:57.000000000 -0700
@@ -19,6 +19,7 @@
 #else
 #define pcibios_assign_all_busses()	0
 #endif
+#define pcibios_scan_all_fns()		0
 
 #define PCIBIOS_MIN_IO		0x1000
 #define PCIBIOS_MIN_MEM		0x10000000
diff -urN linux-2.4.23-pre5/include/asm-mips64/pci.h linux-2.4.23-pre6/include/asm-mips64/pci.h
--- linux-2.4.23-pre5/include/asm-mips64/pci.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-mips64/pci.h	2003-10-01 10:38:57.000000000 -0700
@@ -19,6 +19,7 @@
 #else
 #define pcibios_assign_all_busses()	0
 #endif
+#define pcibios_scan_all_fns()		0
 
 #define PCIBIOS_MIN_IO		0x1000
 #define PCIBIOS_MIN_MEM		0x10000000
diff -urN linux-2.4.23-pre5/include/asm-parisc/pci.h linux-2.4.23-pre6/include/asm-parisc/pci.h
--- linux-2.4.23-pre5/include/asm-parisc/pci.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-parisc/pci.h	2003-10-01 10:38:57.000000000 -0700
@@ -259,6 +259,7 @@
 **   to zero for legacy platforms and one for PAT platforms.
 */
 #define pcibios_assign_all_busses()     (pdc_type == PDC_TYPE_PAT)
+#define pcibios_scan_all_fns()		0
 
 #define PCIBIOS_MIN_IO          0x10
 #define PCIBIOS_MIN_MEM         0x1000 /* NBPG - but pci/setup-res.c dies */
diff -urN linux-2.4.23-pre5/include/asm-ppc/commproc.h linux-2.4.23-pre6/include/asm-ppc/commproc.h
--- linux-2.4.23-pre5/include/asm-ppc/commproc.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-ppc/commproc.h	2003-10-01 10:38:57.000000000 -0700
@@ -466,6 +466,23 @@
 #define SICR_ENET_CLKRT	((uint)0x0000003d)
 #endif	/* CONFIG_RPXCLASSIC */
 
+/***  D-BOX2  ***********************************************/
+
+#ifdef CONFIG_DBOX2
+
+#define PA_ENET_RXD	((ushort)0x0004)
+#define PA_ENET_TXD	((ushort)0x0008)
+#define PA_ENET_RCLK	((ushort)0x0200)
+#define PA_ENET_TCLK	((ushort)0x0800)
+
+#define PC_ENET_TENA	((ushort)0x0002)
+#define PC_ENET_CLSN	((ushort)0x0040)
+#define PC_ENET_RENA	((ushort)0x0080)
+
+#define SICR_ENET_MASK	((uint)0x0000ff00)
+#define SICR_ENET_CLKRT	((uint)0x00003d00)
+#endif	/* CONFIG_DBOX2 */
+
 /***  TQM823L, TQM850L  ***********************************************/
 
 #if defined(CONFIG_TQM823L) || defined(CONFIG_TQM850L)
diff -urN linux-2.4.23-pre5/include/asm-ppc/highmem.h linux-2.4.23-pre6/include/asm-ppc/highmem.h
--- linux-2.4.23-pre5/include/asm-ppc/highmem.h	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-ppc/highmem.h	2003-10-01 10:38:57.000000000 -0700
@@ -41,7 +41,7 @@
  * easily, subsequent pte tables have to be allocated in one physical
  * chunk of RAM.
  */
-#define PKMAP_BASE	(0xfe000000UL)
+#define PKMAP_BASE	CONFIG_HIGHMEM_START
 #define LAST_PKMAP	PTRS_PER_PTE
 #define LAST_PKMAP_MASK (LAST_PKMAP-1)
 #define PKMAP_NR(virt)  ((virt-PKMAP_BASE) >> PAGE_SHIFT)
diff -urN linux-2.4.23-pre5/include/asm-ppc/mpc8xx.h linux-2.4.23-pre6/include/asm-ppc/mpc8xx.h
--- linux-2.4.23-pre5/include/asm-ppc/mpc8xx.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-ppc/mpc8xx.h	2003-10-01 10:38:57.000000000 -0700
@@ -32,6 +32,10 @@
 #include <platforms/rpxclassic.h>
 #endif
 
+#ifdef CONFIG_DBOX2
+#include <platforms/dbox2.h>
+#endif
+
 #if defined(CONFIG_TQM8xxL)
 #include <platforms/tqm8xx.h>
 #endif
diff -urN linux-2.4.23-pre5/include/asm-ppc/page.h linux-2.4.23-pre6/include/asm-ppc/page.h
--- linux-2.4.23-pre5/include/asm-ppc/page.h	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-ppc/page.h	2003-10-01 10:38:57.000000000 -0700
@@ -13,8 +13,7 @@
 #ifdef __KERNEL__
 #include <linux/config.h>
 
-/* Be sure to change arch/ppc/Makefile to match */
-#define PAGE_OFFSET	0xc0000000
+#define PAGE_OFFSET	CONFIG_KERNEL_START
 #define KERNELBASE	PAGE_OFFSET
 
 #ifndef __ASSEMBLY__
diff -urN linux-2.4.23-pre5/include/asm-ppc/pci.h linux-2.4.23-pre6/include/asm-ppc/pci.h
--- linux-2.4.23-pre5/include/asm-ppc/pci.h	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-ppc/pci.h	2003-10-01 10:38:57.000000000 -0700
@@ -24,6 +24,7 @@
 extern int pci_assign_all_busses;
 
 #define pcibios_assign_all_busses()	(pci_assign_all_busses)
+#define pcibios_scan_all_fns()		0
 
 #define PCIBIOS_MIN_IO		0x1000
 #define PCIBIOS_MIN_MEM		0x10000000
diff -urN linux-2.4.23-pre5/include/asm-ppc/pgtable.h linux-2.4.23-pre6/include/asm-ppc/pgtable.h
--- linux-2.4.23-pre5/include/asm-ppc/pgtable.h	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-ppc/pgtable.h	2003-10-01 10:38:57.000000000 -0700
@@ -279,7 +279,7 @@
 #define	_PAGE_WRITETHRU	0x00000800		/* H: W bit */
 
 /* TODO: Add large page lowmem mapping support */
-#define _PMD_PRESENT	0
+#define _PMD_PRESENT	PAGE_MASK
 #define _PMD_PRESENT_MASK (PAGE_MASK)
 #define _PMD_BAD	(~PAGE_MASK)
 
diff -urN linux-2.4.23-pre5/include/asm-ppc/processor.h linux-2.4.23-pre6/include/asm-ppc/processor.h
--- linux-2.4.23-pre5/include/asm-ppc/processor.h	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-ppc/processor.h	2003-10-01 10:38:57.000000000 -0700
@@ -797,7 +797,7 @@
  * as soon as I get around to remapping the io areas with the BATs
  * to match the mac we can raise this. -- Cort
  */
-#define TASK_SIZE	(0x80000000UL)
+#define TASK_SIZE	CONFIG_TASK_SIZE
 
 /* This decides where the kernel will search for a free chunk of vm
  * space during mmap's.
diff -urN linux-2.4.23-pre5/include/asm-ppc/uaccess.h linux-2.4.23-pre6/include/asm-ppc/uaccess.h
--- linux-2.4.23-pre5/include/asm-ppc/uaccess.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-ppc/uaccess.h	2003-10-01 10:38:57.000000000 -0700
@@ -77,16 +77,28 @@
  * As we use the same address space for kernel and user data on the
  * PowerPC, we can just do these as direct assignments.  (Of course, the
  * exception handling means that it's no longer "just"...)
+ *
+ * The "user64" versions of the user access functions are versions that 
+ * allow access of 64-bit data. The "get_user" functions do not 
+ * properly handle 64-bit data because the value gets down cast to a long. 
+ * The "put_user" functions already handle 64-bit data properly but we add 
+ * "user64" versions for completeness
  */
 #define get_user(x,ptr) \
   __get_user_check((x),(ptr),sizeof(*(ptr)))
+#define get_user64(x,ptr) \
+  __get_user64_check((x),(ptr),sizeof(*(ptr)))
 #define put_user(x,ptr) \
   __put_user_check((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr)))
+#define put_user64(x,ptr) put_user(x,ptr)
 
 #define __get_user(x,ptr) \
   __get_user_nocheck((x),(ptr),sizeof(*(ptr)))
+#define __get_user64(x,ptr) \
+  __get_user64_nocheck((x),(ptr),sizeof(*(ptr)))
 #define __put_user(x,ptr) \
   __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr)))
+#define __put_user64(x,ptr) __put_user(x,ptr)
 
 extern long __put_user_bad(void);
 
@@ -166,6 +178,15 @@
 	__gu_err;						\
 })
 
+#define __get_user64_nocheck(x,ptr,size)			\
+({								\
+	long __gu_err;						\
+	long long __gu_val;					\
+	__get_user_size64(__gu_val,(ptr),(size),__gu_err);	\
+	(x) = (__typeof__(*(ptr)))__gu_val;			\
+	__gu_err;						\
+})
+
 #define __get_user_check(x,ptr,size)					\
 ({									\
 	long __gu_err = -EFAULT, __gu_val = 0;				\
@@ -176,6 +197,17 @@
 	__gu_err;							\
 })
 
+#define __get_user64_check(x,ptr,size)					\
+({									\
+	long __gu_err = -EFAULT;					\
+	long long __gu_val = 0;						\
+	const __typeof__(*(ptr)) *__gu_addr = (ptr);			\
+	if (access_ok(VERIFY_READ,__gu_addr,size))			\
+		__get_user_size64(__gu_val,__gu_addr,(size),__gu_err);	\
+	(x) = (__typeof__(*(ptr)))__gu_val;				\
+	__gu_err;							\
+})
+
 extern long __get_user_bad(void);
 
 #define __get_user_size(x,ptr,size,retval)			\
@@ -185,6 +217,17 @@
 	  case 1: __get_user_asm(x,ptr,retval,"lbz"); break;	\
 	  case 2: __get_user_asm(x,ptr,retval,"lhz"); break;	\
 	  case 4: __get_user_asm(x,ptr,retval,"lwz"); break;	\
+	  default: (x) = __get_user_bad();			\
+	}							\
+} while (0)
+
+#define __get_user_size64(x,ptr,size,retval)			\
+do {								\
+	retval = 0;						\
+	switch (size) {						\
+	  case 1: __get_user_asm(x,ptr,retval,"lbz"); break;	\
+	  case 2: __get_user_asm(x,ptr,retval,"lhz"); break;	\
+	  case 4: __get_user_asm(x,ptr,retval,"lwz"); break;	\
 	  case 8: __get_user_asm2(x, ptr, retval); break;	\
 	  default: (x) = __get_user_bad();			\
 	}							\
diff -urN linux-2.4.23-pre5/include/asm-ppc64/pci.h linux-2.4.23-pre6/include/asm-ppc64/pci.h
--- linux-2.4.23-pre5/include/asm-ppc64/pci.h	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.23-pre6/include/asm-ppc64/pci.h	2003-10-01 10:38:57.000000000 -0700
@@ -25,6 +25,12 @@
 #define PCIBIOS_MIN_IO		0x1000
 #define PCIBIOS_MIN_MEM		0x10000000
 
+/*
+ * ppc64 can have multifunction devices that do not respond to function 0.
+ * In this case we must scan all functions.
+ */
+#define pcibios_scan_all_fns()     1
+
 static inline void pcibios_set_master(struct pci_dev *dev)
 {
 	/* No special bus mastering setup handling */
diff -urN linux-2.4.23-pre5/include/asm-sh/checksum.h linux-2.4.23-pre6/include/asm-sh/checksum.h
--- linux-2.4.23-pre5/include/asm-sh/checksum.h	2001-09-08 12:29:09.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-sh/checksum.h	2003-10-01 10:38:57.000000000 -0700
@@ -169,7 +169,6 @@
 }
 
 #define _HAVE_ARCH_IPV6_CSUM
-#ifdef CONFIG_IPV6
 static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
 						     struct in6_addr *daddr,
 						     __u32 len,
@@ -205,7 +204,6 @@
 
 	return csum_fold(sum);
 }
-#endif
 
 /* 
  *	Copy and checksum to user
diff -urN linux-2.4.23-pre5/include/asm-sh/delay.h linux-2.4.23-pre6/include/asm-sh/delay.h
--- linux-2.4.23-pre5/include/asm-sh/delay.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-sh/delay.h	2003-10-01 10:38:57.000000000 -0700
@@ -8,6 +8,7 @@
  */
  
 extern void __bad_udelay(void);
+extern void __bad_ndelay(void);
 
 extern void __udelay(unsigned long usecs);
 extern void __ndelay(unsigned long nsecs);
diff -urN linux-2.4.23-pre5/include/asm-sh/div64.h linux-2.4.23-pre6/include/asm-sh/div64.h
--- linux-2.4.23-pre5/include/asm-sh/div64.h	2000-03-05 09:33:55.000000000 -0800
+++ linux-2.4.23-pre6/include/asm-sh/div64.h	2003-10-01 10:38:57.000000000 -0700
@@ -1,10 +1,20 @@
 #ifndef __ASM_SH_DIV64
 #define __ASM_SH_DIV64
 
+extern u64 __xdiv64_32(u64 n, u32 d);
+
 #define do_div(n,base) ({ \
-int __res; \
-__res = ((unsigned long) n) % (unsigned) base; \
-n = ((unsigned long) n) / (unsigned) base; \
+u64 __n = (n), __q; \
+u32 __base = (base); \
+u32 __res; \
+if ((__n >> 32) == 0) { \
+	__res = ((unsigned long) __n) % (unsigned) __base; \
+	(n) = ((unsigned long) __n) / (unsigned) __base; \
+} else { \
+	__q = __xdiv64_32(__n, __base); \
+	__res = __n - __q * __base; \
+	(n) = __q; \
+} \
 __res; })
 
 #endif /* __ASM_SH_DIV64 */
diff -urN linux-2.4.23-pre5/include/asm-sh/io.h linux-2.4.23-pre6/include/asm-sh/io.h
--- linux-2.4.23-pre5/include/asm-sh/io.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-sh/io.h	2003-10-01 10:38:57.000000000 -0700
@@ -135,6 +135,8 @@
 #  include <asm/io_adx.h>
 # elif defined(CONFIG_SH_SECUREEDGE5410)
 #  include <asm/io_snapgear.h>
+# elif defined(CONFIG_SH_SH4202_MICRODEV)
+#  include <asm/io_microdev.h>
 # elif defined(CONFIG_SH_UNKNOWN)
 #  include <asm/io_unknown.h>
 # else
diff -urN linux-2.4.23-pre5/include/asm-sh/io_microdev.h linux-2.4.23-pre6/include/asm-sh/io_microdev.h
--- linux-2.4.23-pre5/include/asm-sh/io_microdev.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/include/asm-sh/io_microdev.h	2003-10-01 10:38:57.000000000 -0700
@@ -0,0 +1,82 @@
+/*
+ * linux/include/asm-sh/io_microdev.h
+ *
+ * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com)
+ *
+ * IO functions for the SuperH SH4-202 MicroDev board.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ */
+
+
+#ifndef _ASM_SH_IO_MICRODEV_H
+#define _ASM_SH_IO_MICRODEV_H
+
+#include <asm/io_generic.h>
+
+extern unsigned long microdev_isa_port2addr(unsigned long offset);
+
+extern unsigned char microdev_inb(unsigned long port);
+extern unsigned short microdev_inw(unsigned long port);
+extern unsigned int microdev_inl(unsigned long port);
+
+extern void microdev_outb(unsigned char value, unsigned long port);
+extern void microdev_outw(unsigned short value, unsigned long port);
+extern void microdev_outl(unsigned int value, unsigned long port);
+
+extern unsigned char microdev_inb_p(unsigned long port);
+extern unsigned short microdev_inw_p(unsigned long port);
+extern unsigned int microdev_inl_p(unsigned long port);
+
+extern void microdev_outb_p(unsigned char value, unsigned long port);
+extern void microdev_outw_p(unsigned short value, unsigned long port);
+extern void microdev_outl_p(unsigned int value, unsigned long port);
+
+extern void microdev_insb(unsigned long port, void *addr, unsigned long count);
+extern void microdev_insw(unsigned long port, void *addr, unsigned long count);
+extern void microdev_insl(unsigned long port, void *addr, unsigned long count);
+
+extern void microdev_outsb(unsigned long port, const void *addr, unsigned long count);
+extern void microdev_outsw(unsigned long port, const void *addr, unsigned long count);
+extern void microdev_outsl(unsigned long port, const void *addr, unsigned long count);
+
+#ifdef __WANT_IO_DEF
+
+# define __inb			microdev_inb
+# define __inw			microdev_inw
+# define __inl			microdev_inl
+# define __outb			microdev_outb
+# define __outw			microdev_outw
+# define __outl			microdev_outl
+
+# define __inb_p		microdev_inb_p
+# define __inw_p		microdev_inw_p
+# define __inl_p		microdev_inl_p
+# define __outb_p		microdev_outb_p
+# define __outw_p		microdev_outw_p
+# define __outl_p		microdev_outl_p
+
+# define __insb			microdev_insb
+# define __insw			microdev_insw
+# define __insl			microdev_insl
+# define __outsb		microdev_outsb
+# define __outsw		microdev_outsw
+# define __outsl		microdev_outsl
+
+# define __readb		generic_readb
+# define __readw		generic_readw
+# define __readl		generic_readl
+# define __writeb		generic_writeb
+# define __writew		generic_writew
+# define __writel		generic_writel
+
+# define __isa_port2addr	microdev_isa_port2addr
+# define __ioremap		generic_ioremap
+# define __iounmap		generic_iounmap
+
+#endif
+
+#endif /* _ASM_SH_IO_MICRODEV_H */
+
diff -urN linux-2.4.23-pre5/include/asm-sh/irq.h linux-2.4.23-pre6/include/asm-sh/irq.h
--- linux-2.4.23-pre5/include/asm-sh/irq.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-sh/irq.h	2003-10-01 10:38:57.000000000 -0700
@@ -86,7 +86,7 @@
 #define IRDA_IPR_POS	2
 #define IRDA_PRIORITY	3
 #elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751) || \
-      defined(CONFIG_CPU_SUBTYPE_ST40)
+      defined(CONFIG_CPU_SUBTYPE_ST40) || defined(CONFIG_CPU_SUBTYPE_SH4_202)
 #define SCIF_ERI_IRQ	40
 #define SCIF_RXI_IRQ	41
 #define SCIF_BRI_IRQ	42
@@ -127,6 +127,8 @@
 #  define ONCHIP_NR_IRQS 48	// Actually 44
 # elif defined(CONFIG_CPU_SUBTYPE_SH7751)
 #  define ONCHIP_NR_IRQS 72
+# elif defined(CONFIG_CPU_SUBTYPE_SH4_202)
+#  define ONCHIP_NR_IRQS 72
 # elif defined(CONFIG_CPU_SUBTYPE_ST40STB1)
 #  define ONCHIP_NR_IRQS 144
 # elif defined(CONFIG_CPU_SUBTYPE_ST40GX1)
@@ -275,7 +277,7 @@
 #endif /* CONFIG_CPU_SUBTYPE_SH7707 || CONFIG_CPU_SUBTYPE_SH7709 */
 
 #if defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751) || \
-    defined(CONFIG_CPU_SUBTYPE_ST40)
+    defined(CONFIG_CPU_SUBTYPE_ST40) || defined(CONFIG_CPU_SUBTYPE_SH4_202)
 #define INTC_ICR        0xffd00000
 #define INTC_ICR_NMIL	(1<<15)
 #define INTC_ICR_MAI	(1<<14)
diff -urN linux-2.4.23-pre5/include/asm-sh/irq_microdev.h linux-2.4.23-pre6/include/asm-sh/irq_microdev.h
--- linux-2.4.23-pre5/include/asm-sh/irq_microdev.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/include/asm-sh/irq_microdev.h	2003-10-01 10:38:57.000000000 -0700
@@ -0,0 +1,38 @@
+/*
+ * linux/include/asm-sh/irq_microdev.h
+ *
+ * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com)
+ *
+ * IRQ functions for the SuperH SH4-202 MicroDev board.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ */
+
+
+#ifndef _ASM_SH_IRQ_MICRODEV_H
+#define _ASM_SH_IRQ_MICRODEV_H
+
+extern void __init init_microdev_irq(void);
+
+
+	/*
+	 *	The following are useful macros for manipulating the
+	 *	interrupt controller (INTC) on the CPU-board FPGA.
+	 *	It should be noted that there is an INTC on the FPGA,
+	 *	and a seperate INTC on the SH4-202 core - these are
+	 *	two different things, both of which need to be prorammed
+	 *	to correctly route - unfortunately, they have the
+	 *	same name and abbreviations!
+	 */
+#define	MICRODEV_FPGA_INTC_BASE		0xa6110000ul				/* INTC base address on CPU-board FPGA */
+#define	MICRODEV_FPGA_INTENB_REG	(MICRODEV_FPGA_INTC_BASE+0ul)		/* Interrupt Enable Register on INTC on CPU-board FPGA */
+#define	MICRODEV_FPGA_INTDSB_REG	(MICRODEV_FPGA_INTC_BASE+8ul)		/* Interrupt Disable Register on INTC on CPU-board FPGA */
+#define	MICRODEV_FPGA_INTC_MASK(n)	(1ul<<(n))				/* Interupt mask to enable/disable INTC in CPU-board FPGA */
+#define	MICRODEV_FPGA_INTPRI_REG(n)	(MICRODEV_FPGA_INTC_BASE+0x10+((n)/8)*8)/* Interrupt Priority Register on INTC on CPU-board FPGA */
+#define	MICRODEV_FPGA_INTPRI_LEVEL(n,x)	((x)<<(((n)%8)*4))			/* MICRODEV_FPGA_INTPRI_LEVEL(int_number, int_level) */
+#define	MICRODEV_FPGA_INTPRI_MASK(n)	(MICRODEV_FPGA_INTPRI_LEVEL((n),0xful))	/* Interrupt Priority Mask on INTC on CPU-board FPGA */
+
+
+#endif /* _ASM_SH_IRQ_MICRODEV_H */
diff -urN linux-2.4.23-pre5/include/asm-sh/machvec.h linux-2.4.23-pre6/include/asm-sh/machvec.h
--- linux-2.4.23-pre5/include/asm-sh/machvec.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-sh/machvec.h	2003-10-01 10:38:57.000000000 -0700
@@ -81,6 +81,7 @@
 	unsigned int mv_hw_7751se: 1;
 	unsigned int mv_hw_adx : 1;
 	unsigned int mv_hw_snapgear : 1;
+	unsigned int mv_hw_sh4202_microdev : 1;
 };
 
 extern struct sh_machine_vector sh_mv;
@@ -102,6 +103,7 @@
 #define MACH_7751SE	(sh_mv.mv_hw_7751se)
 #define MACH_ADX	(sh_mv.mv_hw_adx)
 #define MACH_SNAPGEAR	(sh_mv.mv_snapgear)
+#define MACH_SH4202_MICRODEV	(sh_mv.mv_hw_sh4202_microdev)
 #else
 # ifdef CONFIG_SH_SOLUTION_ENGINE
 #  define MACH_SE		1
@@ -183,6 +185,11 @@
 # else
 #  define MACH_SNAPGEAR		0
 # endif
+# ifdef CONFIG_SH_SH4202_MICRODEV
+#  define MACH_SH4202_MICRODEV	1
+# else
+#  define MACH_SH4202_MICRODEV	0
+# endif
 #endif
 
 #endif /* _ASM_SH_MACHVEC_H */
diff -urN linux-2.4.23-pre5/include/asm-sh/pci.h linux-2.4.23-pre6/include/asm-sh/pci.h
--- linux-2.4.23-pre5/include/asm-sh/pci.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-sh/pci.h	2003-10-01 10:38:57.000000000 -0700
@@ -10,6 +10,7 @@
    or architectures with incomplete PCI setup by the loader */
 
 #define pcibios_assign_all_busses()	1
+#define pcibios_scan_all_fns()		0
 
 #if defined(CONFIG_CPU_SUBTYPE_ST40)
 /* These are currently the correct values for the ST40 based chips.
diff -urN linux-2.4.23-pre5/include/asm-sh/processor.h linux-2.4.23-pre6/include/asm-sh/processor.h
--- linux-2.4.23-pre5/include/asm-sh/processor.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-sh/processor.h	2003-10-01 10:38:57.000000000 -0700
@@ -23,8 +23,9 @@
 enum cpu_type {
 	CPU_SH7708,		/* Represents 7707, 7708, 7708S, 7708R, 7709 */
 	CPU_SH7729,		/* Represents 7709A, 7729 */
-	CPU_SH7750,     	/* Represents 7750, 7751 */
+	CPU_SH7750,             /* Represents 7750, 7751 */
 	CPU_ST40,		/* Represents ST40STB1 and ST40GX1 */
+        CPU_SH4202,
 	CPU_SH_NONE
 };
 
diff -urN linux-2.4.23-pre5/include/asm-sh64/processor.h linux-2.4.23-pre6/include/asm-sh64/processor.h
--- linux-2.4.23-pre5/include/asm-sh64/processor.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-sh64/processor.h	2003-10-01 10:38:57.000000000 -0700
@@ -113,11 +113,12 @@
 #ifndef __ASSEMBLY__
 
 /*
- * FPU structure and data
+ * FPU structure and data : require 8-byte alignment as we need to access it
+   with fld.p, fst.p
  */
 
 struct sh_fpu_hard_struct {
-	unsigned long long fp_regs[32];
+	unsigned long fp_regs[64];
 	unsigned int fpscr;
 	/* long status; * software status information */
 };
@@ -135,6 +136,9 @@
 union sh_fpu_union {
 	struct sh_fpu_hard_struct hard;
 	// struct sh_fpu_soft_struct soft;
+	/* 'hard' itself only produces 32 bit alignment, yet we need
+	   to access it using 64 bit load/store as well. */
+	unsigned long long alignment_dummy;
 };
 
 struct thread_struct {
diff -urN linux-2.4.23-pre5/include/asm-sh64/registers.h linux-2.4.23-pre6/include/asm-sh64/registers.h
--- linux-2.4.23-pre5/include/asm-sh64/registers.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-sh64/registers.h	2003-10-01 10:38:57.000000000 -0700
@@ -188,6 +188,7 @@
 	** defines below are used only in .../arch/sh5/kernel/fpu.c
 	*/
 #	define __f(x)	__str(fr##x)
+#	define __p(x)	__str(fp##x)
 #	define __d(x)	__str(dr##x)
 
 	/*
diff -urN linux-2.4.23-pre5/include/asm-sparc/pci.h linux-2.4.23-pre6/include/asm-sparc/pci.h
--- linux-2.4.23-pre5/include/asm-sparc/pci.h	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.23-pre6/include/asm-sparc/pci.h	2003-10-01 10:38:57.000000000 -0700
@@ -8,6 +8,7 @@
  * or architectures with incomplete PCI setup by the loader.
  */
 #define pcibios_assign_all_busses()	0
+#define pcibios_scan_all_fns()		0
 
 #define PCIBIOS_MIN_IO		0UL
 #define PCIBIOS_MIN_MEM		0UL
diff -urN linux-2.4.23-pre5/include/asm-sparc64/pci.h linux-2.4.23-pre6/include/asm-sparc64/pci.h
--- linux-2.4.23-pre5/include/asm-sparc64/pci.h	2002-02-25 11:38:13.000000000 -0800
+++ linux-2.4.23-pre6/include/asm-sparc64/pci.h	2003-10-01 10:38:57.000000000 -0700
@@ -11,6 +11,7 @@
  * or architectures with incomplete PCI setup by the loader.
  */
 #define pcibios_assign_all_busses()	0
+#define pcibios_scan_all_fns()		0
 
 #define PCIBIOS_MIN_IO		0UL
 #define PCIBIOS_MIN_MEM		0UL
diff -urN linux-2.4.23-pre5/include/asm-x86_64/acpi.h linux-2.4.23-pre6/include/asm-x86_64/acpi.h
--- linux-2.4.23-pre5/include/asm-x86_64/acpi.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-x86_64/acpi.h	2003-10-01 10:38:57.000000000 -0700
@@ -115,7 +115,22 @@
 #endif
 #ifdef CONFIG_X86_IO_APIC
 extern int acpi_ioapic;
+extern int skip_ioapic_setup;
+
+static inline void disable_ioapic_setup(void)
+{
+	
+	skip_ioapic_setup = 1;
+}
+
+static inline int ioapic_setup_disabled(void)
+{
+	return skip_ioapic_setup;
+}
 #else
+static inline void disable_ioapic_setup(void)
+{ }
+
 #define acpi_ioapic 0
 #endif
 
@@ -142,6 +157,8 @@
 
 extern void mp_config_ioapic_for_sci(int irq);
 
+extern int use_acpi_pci;
+
 #endif /*__KERNEL__*/
 
 #endif /*_ASM_ACPI_H*/
diff -urN linux-2.4.23-pre5/include/asm-x86_64/calling.h linux-2.4.23-pre6/include/asm-x86_64/calling.h
--- linux-2.4.23-pre5/include/asm-x86_64/calling.h	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.23-pre6/include/asm-x86_64/calling.h	2003-10-01 10:38:57.000000000 -0700
@@ -8,7 +8,7 @@
 #define R14 8
 #define R13 16
 #define R12 24
-#define RBP 36
+#define RBP 32
 #define RBX 40
 /* arguments: interrupts/non tracing syscalls only save upto here*/
 #define R11 48
diff -urN linux-2.4.23-pre5/include/asm-x86_64/cpufeature.h linux-2.4.23-pre6/include/asm-x86_64/cpufeature.h
--- linux-2.4.23-pre5/include/asm-x86_64/cpufeature.h	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.23-pre6/include/asm-x86_64/cpufeature.h	2003-10-01 10:38:57.000000000 -0700
@@ -63,6 +63,26 @@
 #define X86_FEATURE_CYRIX_ARR	(3*32+ 2) /* Cyrix ARRs (= MTRRs) */
 #define X86_FEATURE_CENTAUR_MCR	(3*32+ 3) /* Centaur MCRs (= MTRRs) */
 
+#define cpu_has(c, bit)                test_bit(bit, (c)->x86_capability)
+#define boot_cpu_has(bit)      test_bit(bit, boot_cpu_data.x86_capability)
+
+#define cpu_has_pge 1
+#define cpu_has_pse 1
+#define cpu_has_pae 1
+#define cpu_has_tsc 1
+#define cpu_has_de 1
+#define cpu_has_vme 1
+#define cpu_has_fxsr 1
+#define cpu_has_xmm 1
+#define cpu_has_apic 		boot_cpu_has(X86_FEATURE_APIC)
+#define cpu_has_ht 0 
+#define cpu_has_mp             boot_cpu_has(X86_FEATURE_MP)
+#define cpu_has_k6_mtrr 0
+#define cpu_has_cyrix_arr 0
+#define cpu_has_centaur_mcr 0
+#define cpu_has_mmx 0
+#define cpu_has_fpu 1
+
 #endif /* __ASM_X8664_CPUFEATURE_H */
 
 /* 
diff -urN linux-2.4.23-pre5/include/asm-x86_64/hardirq.h linux-2.4.23-pre6/include/asm-x86_64/hardirq.h
--- linux-2.4.23-pre5/include/asm-x86_64/hardirq.h	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.23-pre6/include/asm-x86_64/hardirq.h	2003-10-01 10:38:57.000000000 -0700
@@ -67,6 +67,8 @@
 {
 	++local_irq_count(cpu);
 
+	smp_mb();
+	
 	while (test_bit(0,&global_irq_lock)) {
 		cpu_relax();
 	}
diff -urN linux-2.4.23-pre5/include/asm-x86_64/ia32.h linux-2.4.23-pre6/include/asm-x86_64/ia32.h
--- linux-2.4.23-pre5/include/asm-x86_64/ia32.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-x86_64/ia32.h	2003-10-01 10:38:57.000000000 -0700
@@ -234,6 +234,11 @@
 	int iov_len; 
 };
 
+struct timespec32 {
+	int 	tv_sec;
+	int	tv_nsec;
+};
+
 #endif /* !CONFIG_IA32_SUPPORT */
  
 #endif 
diff -urN linux-2.4.23-pre5/include/asm-x86_64/kdebug.h linux-2.4.23-pre6/include/asm-x86_64/kdebug.h
--- linux-2.4.23-pre5/include/asm-x86_64/kdebug.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-x86_64/kdebug.h	2003-10-01 10:38:57.000000000 -0700
@@ -39,11 +39,13 @@
 	return notifier_call_chain(&die_chain, val, &args); 
 } 
 
-
 extern int printk_address(unsigned long address);
 extern void die(const char *,struct pt_regs *,long);
+extern void __die(const char *,struct pt_regs *,long);
 extern void show_stack(unsigned long* esp);
 extern void show_registers(struct pt_regs *regs);
 extern void dump_pagetable(unsigned long);
+extern void prepare_die(unsigned long *flags);
+extern void exit_die(unsigned long flags);
 
 #endif
diff -urN linux-2.4.23-pre5/include/asm-x86_64/pci.h linux-2.4.23-pre6/include/asm-x86_64/pci.h
--- linux-2.4.23-pre5/include/asm-x86_64/pci.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-x86_64/pci.h	2003-10-01 10:38:57.000000000 -0700
@@ -17,6 +17,7 @@
 #else
 #define pcibios_assign_all_busses()	0
 #endif
+#define pcibios_scan_all_fns()		0
 
 extern unsigned long pci_mem_start;
 #define PCIBIOS_MIN_IO		0x1000
diff -urN linux-2.4.23-pre5/include/asm-x86_64/pgalloc.h linux-2.4.23-pre6/include/asm-x86_64/pgalloc.h
--- linux-2.4.23-pre5/include/asm-x86_64/pgalloc.h	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.23-pre6/include/asm-x86_64/pgalloc.h	2003-10-01 10:38:57.000000000 -0700
@@ -204,7 +204,7 @@
 {
 	struct mm_struct *active_mm;
 	int state;
-};
+} ____cacheline_aligned;
 extern struct tlb_state cpu_tlbstate[NR_CPUS];
 
 
diff -urN linux-2.4.23-pre5/include/asm-x86_64/processor.h linux-2.4.23-pre6/include/asm-x86_64/processor.h
--- linux-2.4.23-pre5/include/asm-x86_64/processor.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-x86_64/processor.h	2003-10-01 10:38:57.000000000 -0700
@@ -17,6 +17,7 @@
 #include <asm/msr.h>
 #include <asm/current.h>
 #include <asm/system.h>
+#include <asm/cpufeature.h>
 
 #define TF_MASK		0x00000100
 #define IF_MASK		0x00000200
@@ -77,16 +78,6 @@
 #define current_cpu_data boot_cpu_data
 #endif
 
-#define cpu_has_pge 1
-#define cpu_has_pse 1
-#define cpu_has_pae 1
-#define cpu_has_tsc 1
-#define cpu_has_de 1
-#define cpu_has_vme 1
-#define cpu_has_fxsr 1
-#define cpu_has_xmm 1
-#define cpu_has_apic (test_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability))
-
 extern char ignore_irq13;
 
 extern void identify_cpu(struct cpuinfo_x86 *);
@@ -258,7 +249,9 @@
 /* This decides where the kernel will search for a free chunk of vm
  * space during mmap's.
  */
-#define TASK_UNMAPPED_32 0xa0000000
+
+#define IA32_PAGE_OFFSET ((current->personality & ADDR_LIMIT_3GB) ? 0xc0000000 : 0xFFFFe000)
+#define TASK_UNMAPPED_32 (IA32_PAGE_OFFSET / 3) 
 #define TASK_UNMAPPED_64 (TASK_SIZE/3) 
 #define TASK_UNMAPPED_BASE	\
 	((current->thread.flags & THREAD_IA32) ? TASK_UNMAPPED_32 : TASK_UNMAPPED_64)  
@@ -396,7 +389,7 @@
 /* REP NOP (PAUSE) is a good thing to insert into busy-wait loops. */
 extern inline void rep_nop(void)
 {
-	__asm__ __volatile__("rep;nop");
+	__asm__ __volatile__("rep;nop":::"memory");
 }
 
 /* Avoid speculative execution by the CPU */
@@ -406,8 +399,6 @@
 	asm volatile("cpuid" : "=a" (tmp) : "0" (1) : "ebx","ecx","edx","memory");
 } 
 
-#define cpu_has_fpu 1
-
 #define ARCH_HAS_PREFETCH
 #define ARCH_HAS_PREFETCHW
 #define ARCH_HAS_SPINLOCK_PREFETCH
diff -urN linux-2.4.23-pre5/include/asm-x86_64/proto.h linux-2.4.23-pre6/include/asm-x86_64/proto.h
--- linux-2.4.23-pre5/include/asm-x86_64/proto.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-x86_64/proto.h	2003-10-01 10:38:57.000000000 -0700
@@ -43,6 +43,8 @@
 extern void __show_regs(struct pt_regs * regs);
 extern void show_regs(struct pt_regs * regs);
 
+extern int apic_disabled;
+extern int acpi_disabled;
 
 #define round_up(x,y) (((x) + (y) - 1) & ~((y)-1))
 #define round_down(x,y) ((x) & ~((y)-1))
diff -urN linux-2.4.23-pre5/include/asm-x86_64/ptrace.h linux-2.4.23-pre6/include/asm-x86_64/ptrace.h
--- linux-2.4.23-pre5/include/asm-x86_64/ptrace.h	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.23-pre6/include/asm-x86_64/ptrace.h	2003-10-01 10:38:57.000000000 -0700
@@ -6,7 +6,7 @@
 #define R14 8
 #define R13 16
 #define R12 24
-#define RBP 36
+#define RBP 32
 #define RBX 40
 /* arguments: interrupts/non tracing syscalls only save upto here*/
 #define R11 48
@@ -37,10 +37,6 @@
 /* options set using PTRACE_SETOPTIONS */
 #define PTRACE_O_TRACESYSGOOD     0x00000001
 
-/* Dummy values for ptrace */ 
-#define FS 1000 
-#define GS 1008
-
 #ifndef __ASSEMBLY__ 
 
 struct pt_regs {
diff -urN linux-2.4.23-pre5/include/asm-x86_64/system.h linux-2.4.23-pre6/include/asm-x86_64/system.h
--- linux-2.4.23-pre5/include/asm-x86_64/system.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-x86_64/system.h	2003-10-01 10:38:57.000000000 -0700
@@ -284,8 +284,8 @@
 #define sti() __global_sti()
 #define save_flags(x) ((x)=__global_save_flags())
 #define restore_flags(x) __global_restore_flags(x)
-#define save_and_cli(x) do { save_flags(x); cli(); } while(0);
-#define save_and_sti(x) do { save_flags(x); sti(); } while(0);
+#define save_and_cli(x) do { save_flags(x); cli(); } while(0)
+#define save_and_sti(x) do { save_flags(x); sti(); } while(0)
 
 #else
 
diff -urN linux-2.4.23-pre5/include/asm-x86_64/uaccess.h linux-2.4.23-pre6/include/asm-x86_64/uaccess.h
--- linux-2.4.23-pre5/include/asm-x86_64/uaccess.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-x86_64/uaccess.h	2003-10-01 10:38:57.000000000 -0700
@@ -254,12 +254,12 @@
 	case 10:
 	       	__get_user_asm(*(u64*)dst,(u64*)src,ret,"q","","=r",16);
 		if (ret) return ret;
-		__get_user_asm(*(u16*)(8+dst),(u16*)(8+src),ret,"w","w","=r",2);
+		__get_user_asm(*(u16*)(8+(char*)dst),(u16*)(8+(char*)src),ret,"w","w","=r",2);
 		return ret; 
 	case 16:
 		__get_user_asm(*(u64*)dst,(u64*)src,ret,"q","","=r",16);
 		if (ret) return ret;
-		__get_user_asm(*(u64*)(8+dst),(u64*)(8+src),ret,"q","","=r",8);
+		__get_user_asm(*(u64*)(8+(char*)dst),(u64*)(8+(char*)src),ret,"q","","=r",8);
 		return ret; 
 	default:
 		return copy_user_generic(dst,src,size); 
diff -urN linux-2.4.23-pre5/include/asm-x86_64/unistd.h linux-2.4.23-pre6/include/asm-x86_64/unistd.h
--- linux-2.4.23-pre5/include/asm-x86_64/unistd.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.23-pre6/include/asm-x86_64/unistd.h	2003-10-01 10:38:57.000000000 -0700
@@ -462,7 +462,7 @@
 #define __NR_tkill	200	/* 2.5 only */
 __SYSCALL(__NR_tkill, sys_ni_syscall) 
 #define __NR_time		201
-__SYSCALL(__NR_time, sys_time)
+__SYSCALL(__NR_time, sys_time64)
 #define __NR_futex		202 /* 2.5 only */
 __SYSCALL(__NR_futex, sys_ni_syscall)
 #define __NR_sched_setaffinity    203
@@ -495,8 +495,12 @@
 __SYSCALL(__NR_remap_file_pages, sys_ni_syscall)
 #define __NR_getdents64 217
 __SYSCALL(__NR_getdents64, sys_getdents64)
+#define __NR_restart_syscall	219
+__SYSCALL(__NR_restart_syscall, sys_ni_syscall)
+#define __NR_semtimedop		220
+__SYSCALL(__NR_semtimedop, sys_semtimedop)
 
-#define __NR_syscall_max __NR_getdents64
+#define __NR_syscall_max __NR_semtimedop
 
 #ifndef __NO_STUBS
 
diff -urN linux-2.4.23-pre5/include/linux/agp_backend.h linux-2.4.23-pre6/include/linux/agp_backend.h
--- linux-2.4.23-pre5/include/linux/agp_backend.h	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/include/linux/agp_backend.h	2003-10-01 10:38:57.000000000 -0700
@@ -92,6 +92,7 @@
 	SVWRKS_GENERIC,
 	NVIDIA_NFORCE,
 	NVIDIA_NFORCE2,
+	NVIDIA_NFORCE3,
 	NVIDIA_GENERIC,
 	HP_ZX1,
 	ATI_RS100,
diff -urN linux-2.4.23-pre5/include/linux/in.h linux-2.4.23-pre6/include/linux/in.h
--- linux-2.4.23-pre5/include/linux/in.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/linux/in.h	2003-10-01 10:38:57.000000000 -0700
@@ -41,6 +41,7 @@
   IPPROTO_ESP = 50,            /* Encapsulation Security Payload protocol */
   IPPROTO_AH = 51,             /* Authentication Header protocol       */
   IPPROTO_COMP   = 108,                /* Compression Header protocol */
+  IPPROTO_SCTP   = 132,		/* Stream Control Transport Protocol    */
 
   IPPROTO_RAW	 = 255,		/* Raw IP packets			*/
   IPPROTO_MAX
diff -urN linux-2.4.23-pre5/include/linux/ipv6.h linux-2.4.23-pre6/include/linux/ipv6.h
--- linux-2.4.23-pre5/include/linux/ipv6.h	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/include/linux/ipv6.h	2003-10-01 10:38:57.000000000 -0700
@@ -100,6 +100,52 @@
 	struct	in6_addr	daddr;
 };
 
+/*
+ * This structure contains configuration options per IPv6 link.
+ */
+struct ipv6_devconf {
+	__s32		forwarding;
+	__s32		hop_limit;
+	__s32		mtu6;
+	__s32		accept_ra;
+	__s32		accept_redirects;
+	__s32		autoconf;
+	__s32		dad_transmits;
+	__s32		rtr_solicits;
+	__s32		rtr_solicit_interval;
+	__s32		rtr_solicit_delay;
+#ifdef CONFIG_IPV6_PRIVACY
+	__s32		use_tempaddr;
+	__s32		temp_valid_lft;
+	__s32		temp_prefered_lft;
+	__s32		regen_max_retry;
+	__s32		max_desync_factor;
+#endif
+	void		*sysctl;
+};
+
+/* index values for the variables in ipv6_devconf */
+enum {
+	DEVCONF_FORWARDING = 0,
+	DEVCONF_HOPLIMIT,
+	DEVCONF_MTU6,
+	DEVCONF_ACCEPT_RA,
+	DEVCONF_ACCEPT_REDIRECTS,
+	DEVCONF_AUTOCONF,
+	DEVCONF_DAD_TRANSMITS,
+	DEVCONF_RTR_SOLICITS,
+	DEVCONF_RTR_SOLICIT_INTERVAL,
+	DEVCONF_RTR_SOLICIT_DELAY,
+#ifdef CONFIG_IPV6_PRIVACY
+	DEVCONF_USE_TEMPADDR,
+	DEVCONF_TEMP_VALID_LFT,
+	DEVCONF_TEMP_PREFERED_LFT,
+	DEVCONF_REGEN_MAX_RETRY,
+	DEVCONF_MAX_DESYNC_FACTOR,
+#endif
+	DEVCONF_MAX
+};
+
 #ifdef __KERNEL__
 
 /* 
diff -urN linux-2.4.23-pre5/include/linux/net.h linux-2.4.23-pre6/include/linux/net.h
--- linux-2.4.23-pre5/include/linux/net.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/linux/net.h	2003-10-01 10:38:57.000000000 -0700
@@ -141,6 +141,7 @@
 				  const struct iovec * iov, long count, long size);
 extern struct socket *sockfd_lookup(int fd, int *err);
 
+extern int	sock_map_fd(struct socket *sock);
 extern int	net_ratelimit(void);
 extern unsigned long net_random(void);
 extern void net_srandom(unsigned long);
diff -urN linux-2.4.23-pre5/include/linux/pci_ids.h linux-2.4.23-pre6/include/linux/pci_ids.h
--- linux-2.4.23-pre5/include/linux/pci_ids.h	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/include/linux/pci_ids.h	2003-10-01 10:38:57.000000000 -0700
@@ -559,6 +559,7 @@
 #define PCI_DEVICE_ID_SI_750		0x0750
 #define PCI_DEVICE_ID_SI_751		0x0751
 #define PCI_DEVICE_ID_SI_752		0x0752
+#define PCI_DEVICE_ID_SI_755            0x0755
 #define PCI_DEVICE_ID_SI_900		0x0900
 #define PCI_DEVICE_ID_SI_5107		0x5107
 #define PCI_DEVICE_ID_SI_5300		0x5300
@@ -967,6 +968,7 @@
 #define PCI_DEVICE_ID_NVIDIA_UVTNT2		0x002D
 #define PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE	0x0065
 #define PCI_DEVICE_ID_NVIDIA_ITNT2		0x00A0
+#define PCI_DEVICE_ID_NVIDIA_NFORCE3		0x00d1
 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_SDR	0x0100
 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_DDR	0x0101
 #define PCI_DEVICE_ID_NVIDIA_QUADRO		0x0103
@@ -1091,6 +1093,7 @@
 #define PCI_DEVICE_ID_VIA_P4X333	0x3168
 #define PCI_DEVICE_ID_VIA_8235		0x3177
 #define PCI_DEVICE_ID_VIA_8377_0	0x3189
+#define PCI_DEVICE_ID_VIA_K8T400M_0	0x3188
 #define PCI_DEVICE_ID_VIA_8237		0x3227
 #define PCI_DEVICE_ID_VIA_86C100A	0x6100
 #define PCI_DEVICE_ID_VIA_8231		0x8231
diff -urN linux-2.4.23-pre5/include/linux/personality.h linux-2.4.23-pre6/include/linux/personality.h
--- linux-2.4.23-pre5/include/linux/personality.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/linux/personality.h	2003-10-01 10:38:57.000000000 -0700
@@ -57,6 +57,7 @@
 	PER_SUNOS =		0x0006 | STICKY_TIMEOUTS,
 	PER_XENIX =		0x0007 | STICKY_TIMEOUTS | SHORT_INODE,
 	PER_LINUX32 =		0x0008,
+	PER_LINUX32_3GB =	0x0008 | ADDR_LIMIT_3GB,
 	PER_IRIX32 =		0x0009 | STICKY_TIMEOUTS,/* IRIX5 32-bit */
 	PER_IRIXN32 =		0x000a | STICKY_TIMEOUTS,/* IRIX6 new 32-bit */
 	PER_IRIX64 =		0x000b | STICKY_TIMEOUTS,/* IRIX6 64-bit */
diff -urN linux-2.4.23-pre5/include/linux/rtnetlink.h linux-2.4.23-pre6/include/linux/rtnetlink.h
--- linux-2.4.23-pre5/include/linux/rtnetlink.h	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/include/linux/rtnetlink.h	2003-10-01 10:38:57.000000000 -0700
@@ -445,10 +445,12 @@
 #define IFLA_MASTER IFLA_MASTER
 	IFLA_WIRELESS,		/* Wireless Extension event - see wireless.h */
 #define IFLA_WIRELESS IFLA_WIRELESS
+	IFLA_PROTINFO,		/* Protocol specific information for a link */
+#define IFLA_PROTINFO IFLA_PROTINFO
 };
 
 
-#define IFLA_MAX IFLA_WIRELESS
+#define IFLA_MAX IFLA_PROTINFO
 
 #define IFLA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
 #define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
@@ -482,6 +484,18 @@
    for IPIP tunnels, when route to endpoint is allowed to change)
  */
 
+/* Subtype attributes for IFLA_PROTINFO */
+enum
+{
+	IFLA_INET6_UNSPEC,
+	IFLA_INET6_FLAGS,	/* link flags			*/
+	IFLA_INET6_CONF,	/* sysctl parameters		*/
+	IFLA_INET6_STATS,	/* statistics			*/
+	IFLA_INET6_MCAST,	/* MC things. What of them?	*/
+};
+
+#define IFLA_INET6_MAX	IFLA_INET6_MCAST
+
 /*****************************************************************
  *		Traffic control messages.
  ****/
diff -urN linux-2.4.23-pre5/include/linux/sctp.h linux-2.4.23-pre6/include/linux/sctp.h
--- linux-2.4.23-pre5/include/linux/sctp.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/include/linux/sctp.h	2003-10-01 10:38:57.000000000 -0700
@@ -0,0 +1,588 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001-2002 International Business Machines, Corp.
+ * Copyright (c) 2001 Intel Corp.
+ * Copyright (c) 2001 Nokia, Inc.
+ * Copyright (c) 2001 La Monte H.P. Yarroll
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * Various protocol defined structures.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developerst@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Karl Knutson <karl@athena.chicago.il.us>
+ *    Jon Grimm <jgrimm@us.ibm.com>
+ *    Xingang Guo <xingang.guo@intel.com>
+ *    randall@sctp.chicago.il.us
+ *    kmorneau@cisco.com
+ *    qxie1@email.mot.com
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+#ifndef __LINUX_SCTP_H__
+#define __LINUX_SCTP_H__
+
+#include <linux/in.h>		/* We need in_addr.  */
+#include <linux/in6.h>		/* We need in6_addr.  */
+
+
+/* Section 3.1.  SCTP Common Header Format */
+typedef struct sctphdr {
+	__u16 source;
+	__u16 dest;
+	__u32 vtag;
+	__u32 checksum;
+} sctp_sctphdr_t __attribute__((packed));
+
+/* Section 3.2.  Chunk Field Descriptions. */
+typedef struct sctp_chunkhdr {
+	__u8 type;
+	__u8 flags;
+	__u16 length;
+} sctp_chunkhdr_t __attribute__((packed));
+
+
+/* Section 3.2.  Chunk Type Values.
+ * [Chunk Type] identifies the type of information contained in the Chunk
+ * Value field. It takes a value from 0 to 254. The value of 255 is
+ * reserved for future use as an extension field.
+ */
+typedef enum {
+	SCTP_CID_DATA			= 0,
+        SCTP_CID_INIT			= 1,
+        SCTP_CID_INIT_ACK		= 2,
+        SCTP_CID_SACK			= 3,
+        SCTP_CID_HEARTBEAT		= 4,
+        SCTP_CID_HEARTBEAT_ACK		= 5,
+        SCTP_CID_ABORT			= 6,
+        SCTP_CID_SHUTDOWN		= 7,
+        SCTP_CID_SHUTDOWN_ACK		= 8,
+        SCTP_CID_ERROR			= 9,
+        SCTP_CID_COOKIE_ECHO		= 10,
+        SCTP_CID_COOKIE_ACK	        = 11,
+        SCTP_CID_ECN_ECNE		= 12,
+        SCTP_CID_ECN_CWR		= 13,
+        SCTP_CID_SHUTDOWN_COMPLETE	= 14,
+
+	/* Use hex, as defined in ADDIP sec. 3.1 */
+	SCTP_CID_ASCONF			= 0xC1,
+	SCTP_CID_ASCONF_ACK		= 0x80,
+} sctp_cid_t; /* enum */
+
+
+/* Section 3.2
+ *  Chunk Types are encoded such that the highest-order two bits specify
+ *  the action that must be taken if the processing endpoint does not
+ *  recognize the Chunk Type.
+ */
+typedef enum {
+	SCTP_CID_ACTION_DISCARD     = 0x00,
+	SCTP_CID_ACTION_DISCARD_ERR = 0x40,
+	SCTP_CID_ACTION_SKIP        = 0x80,
+	SCTP_CID_ACTION_SKIP_ERR    = 0xc0,
+} sctp_cid_action_t;
+
+enum { SCTP_CID_ACTION_MASK = 0xc0, };
+
+/* This flag is used in Chunk Flags for ABORT and SHUTDOWN COMPLETE.
+ *
+ * 3.3.7 Abort Association (ABORT) (6):
+ *    The T bit is set to 0 if the sender had a TCB that it destroyed.
+ *    If the sender did not have a TCB it should set this bit to 1.
+ */
+enum { SCTP_CHUNK_FLAG_T = 0x01 };
+
+/*
+ *  Set the T bit
+ *
+ *      0                   1                   2                   3
+ *      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *     |   Type = 14   |Reserved     |T|      Length = 4               |
+ *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Chunk Flags: 8 bits
+ *
+ *   Reserved:  7 bits
+ *     Set to 0 on transmit and ignored on receipt.
+ *
+ *   T bit:  1 bit
+ *     The T bit is set to 0 if the sender had a TCB that it destroyed. If
+ *     the sender did NOT have a TCB it should set this bit to 1.
+ *
+ * Note: Special rules apply to this chunk for verification, please
+ * see Section 8.5.1 for details.
+ */
+
+#define sctp_test_T_bit(c)    ((c)->chunk_hdr->flags & SCTP_CHUNK_FLAG_T)
+
+/* RFC 2960
+ * Section 3.2.1 Optional/Variable-length Parmaeter Format.
+ */
+
+typedef struct sctp_paramhdr {
+	__u16 type;
+	__u16 length;
+} sctp_paramhdr_t __attribute((packed));
+
+typedef enum {
+
+	/* RFC 2960 Section 3.3.5 */
+	SCTP_PARAM_HEARTBEAT_INFO		= __constant_htons(1),
+	/* RFC 2960 Section 3.3.2.1 */
+	SCTP_PARAM_IPV4_ADDRESS			= __constant_htons(5),
+	SCTP_PARAM_IPV6_ADDRESS			= __constant_htons(6),
+	SCTP_PARAM_STATE_COOKIE			= __constant_htons(7),
+	SCTP_PARAM_UNRECOGNIZED_PARAMETERS	= __constant_htons(8),
+	SCTP_PARAM_COOKIE_PRESERVATIVE		= __constant_htons(9),
+	SCTP_PARAM_HOST_NAME_ADDRESS		= __constant_htons(11),
+	SCTP_PARAM_SUPPORTED_ADDRESS_TYPES	= __constant_htons(12),
+	SCTP_PARAM_ECN_CAPABLE			= __constant_htons(0x8000),
+
+	/* Add-IP Extension. Section 3.2 */
+	SCTP_PARAM_ADD_IP		= __constant_htons(0xc001),
+	SCTP_PARAM_DEL_IP		= __constant_htons(0xc002),
+	SCTP_PARAM_ERR_CAUSE		= __constant_htons(0xc003),
+	SCTP_PARAM_SET_PRIMARY		= __constant_htons(0xc004),
+	SCTP_PARAM_SUCCESS_REPORT	= __constant_htons(0xc005),
+	SCTP_PARAM_ADAPTION_LAYER_IND   = __constant_htons(0xc006),
+
+} sctp_param_t; /* enum */
+
+
+/* RFC 2960 Section 3.2.1
+ *  The Parameter Types are encoded such that the highest-order two bits
+ *  specify the action that must be taken if the processing endpoint does
+ *  not recognize the Parameter Type.
+ *
+ */
+typedef enum {
+	SCTP_PARAM_ACTION_DISCARD     = __constant_htons(0x0000),
+	SCTP_PARAM_ACTION_DISCARD_ERR = __constant_htons(0x4000),
+	SCTP_PARAM_ACTION_SKIP        = __constant_htons(0x8000),
+	SCTP_PARAM_ACTION_SKIP_ERR    = __constant_htons(0xc000),
+} sctp_param_action_t;
+
+enum { SCTP_PARAM_ACTION_MASK = __constant_htons(0xc000), };
+
+/* RFC 2960 Section 3.3.1 Payload Data (DATA) (0) */
+
+typedef struct sctp_datahdr {
+	__u32 tsn;
+	__u16 stream;
+	__u16 ssn;
+	__u32 ppid;
+	__u8  payload[0];
+} sctp_datahdr_t __attribute__((packed));
+
+typedef struct sctp_data_chunk {
+        sctp_chunkhdr_t chunk_hdr;
+        sctp_datahdr_t  data_hdr;
+} sctp_data_chunk_t __attribute__((packed));
+
+/* DATA Chuck Specific Flags */
+enum {
+	SCTP_DATA_MIDDLE_FRAG	= 0x00,
+	SCTP_DATA_LAST_FRAG	= 0x01,
+	SCTP_DATA_FIRST_FRAG	= 0x02,
+	SCTP_DATA_NOT_FRAG	= 0x03,
+	SCTP_DATA_UNORDERED	= 0x04,
+};
+enum { SCTP_DATA_FRAG_MASK = 0x03, };
+
+
+/* RFC 2960 Section 3.3.2 Initiation (INIT) (1)
+ *
+ *  This chunk is used to initiate a SCTP association between two
+ *  endpoints.
+ */
+typedef struct sctp_inithdr {
+	__u32 init_tag;
+	__u32 a_rwnd;
+	__u16 num_outbound_streams;
+	__u16 num_inbound_streams;
+	__u32 initial_tsn;
+	__u8  params[0];
+} sctp_inithdr_t __attribute__((packed));
+
+typedef struct sctp_init_chunk {
+	sctp_chunkhdr_t chunk_hdr;
+	sctp_inithdr_t init_hdr;
+} sctp_init_chunk_t __attribute__((packed));
+
+
+/* Section 3.3.2.1. IPv4 Address Parameter (5) */
+typedef struct sctp_ipv4addr_param {
+	sctp_paramhdr_t param_hdr;
+	struct in_addr  addr;
+} sctp_ipv4addr_param_t __attribute__((packed));
+
+/* Section 3.3.2.1. IPv6 Address Parameter (6) */
+typedef struct sctp_ipv6addr_param {
+	sctp_paramhdr_t param_hdr;
+	struct in6_addr addr;
+} sctp_ipv6addr_param_t __attribute__((packed));
+
+/* Section 3.3.2.1 Cookie Preservative (9) */
+typedef struct sctp_cookie_preserve_param {
+	sctp_paramhdr_t param_hdr;
+	uint32_t        lifespan_increment;
+} sctp_cookie_preserve_param_t __attribute__((packed));
+
+/* Section 3.3.2.1 Host Name Address (11) */
+typedef struct sctp_hostname_param {
+	sctp_paramhdr_t param_hdr;
+	uint8_t hostname[0];
+} sctp_hostname_param_t __attribute__((packed));
+
+/* Section 3.3.2.1 Supported Address Types (12) */
+typedef struct sctp_supported_addrs_param {
+	sctp_paramhdr_t param_hdr;
+	uint16_t types[0];
+} sctp_supported_addrs_param_t __attribute__((packed));
+
+/* Appendix A. ECN Capable (32768) */
+typedef struct sctp_ecn_capable_param {
+	sctp_paramhdr_t param_hdr;
+} sctp_ecn_capable_param_t __attribute__((packed));
+
+
+
+/* RFC 2960.  Section 3.3.3 Initiation Acknowledgement (INIT ACK) (2):
+ *   The INIT ACK chunk is used to acknowledge the initiation of an SCTP
+ *   association.
+ */
+typedef sctp_init_chunk_t sctp_initack_chunk_t;
+
+/* Section 3.3.3.1 State Cookie (7) */
+typedef struct sctp_cookie_param {
+	sctp_paramhdr_t p;
+	__u8 body[0];
+} sctp_cookie_param_t __attribute__((packed));
+
+/* Section 3.3.3.1 Unrecognized Parameters (8) */
+typedef struct sctp_unrecognized_param {
+	sctp_paramhdr_t param_hdr;
+	sctp_paramhdr_t unrecognized;
+} sctp_unrecognized_param_t __attribute__((packed));
+
+
+
+/*
+ * 3.3.4 Selective Acknowledgement (SACK) (3):
+ *
+ *  This chunk is sent to the peer endpoint to acknowledge received DATA
+ *  chunks and to inform the peer endpoint of gaps in the received
+ *  subsequences of DATA chunks as represented by their TSNs.
+ */
+
+typedef struct sctp_gap_ack_block {
+	__u16 start;
+	__u16 end;
+} sctp_gap_ack_block_t __attribute__((packed));
+
+typedef uint32_t sctp_dup_tsn_t;
+
+typedef union {
+	sctp_gap_ack_block_t	gab;
+        sctp_dup_tsn_t		dup;
+} sctp_sack_variable_t;
+
+typedef struct sctp_sackhdr {
+	__u32 cum_tsn_ack;
+	__u32 a_rwnd;
+	__u16 num_gap_ack_blocks;
+	__u16 num_dup_tsns;
+	sctp_sack_variable_t variable[0];
+} sctp_sackhdr_t __attribute__((packed));
+
+typedef struct sctp_sack_chunk {
+	sctp_chunkhdr_t chunk_hdr;
+	sctp_sackhdr_t sack_hdr;
+} sctp_sack_chunk_t __attribute__((packed));
+
+
+/* RFC 2960.  Section 3.3.5 Heartbeat Request (HEARTBEAT) (4):
+ *
+ *  An endpoint should send this chunk to its peer endpoint to probe the
+ *  reachability of a particular destination transport address defined in
+ *  the present association.
+ */
+
+typedef struct sctp_heartbeathdr {
+	sctp_paramhdr_t info;
+} sctp_heartbeathdr_t __attribute__((packed));
+
+typedef struct sctp_heartbeat_chunk {
+	sctp_chunkhdr_t chunk_hdr;
+	sctp_heartbeathdr_t hb_hdr;
+} sctp_heartbeat_chunk_t __attribute__((packed));
+
+
+/* For the abort and shutdown ACK we must carry the init tag in the
+ * common header. Just the common header is all that is needed with a
+ * chunk descriptor.
+ */
+typedef struct sctp_abort_chunk {
+        sctp_chunkhdr_t uh;
+} sctp_abort_chunkt_t __attribute__((packed));
+
+
+/* For the graceful shutdown we must carry the tag (in common header)
+ * and the highest consecutive acking value.
+ */
+typedef struct sctp_shutdownhdr {
+	__u32 cum_tsn_ack;
+} sctp_shutdownhdr_t __attribute__((packed));
+
+struct sctp_shutdown_chunk_t {
+        sctp_chunkhdr_t    chunk_hdr;
+        sctp_shutdownhdr_t shutdown_hdr;
+} __attribute__((packed));
+
+
+
+/* RFC 2960.  Section 3.3.10 Operation Error (ERROR) (9) */
+
+typedef struct sctp_errhdr {
+	__u16 cause;
+	__u16 length;
+	__u8  variable[0];
+} sctp_errhdr_t __attribute__((packed));
+
+typedef struct sctp_operr_chunk {
+        sctp_chunkhdr_t chunk_hdr;
+	sctp_errhdr_t   err_hdr;
+} sctp_operr_chunk_t __attribute__((packed));
+
+/* RFC 2960 3.3.10 - Operation Error
+ *
+ * Cause Code: 16 bits (unsigned integer)
+ *
+ *     Defines the type of error conditions being reported.
+ *    Cause Code
+ *     Value           Cause Code
+ *     ---------      ----------------
+ *      1              Invalid Stream Identifier
+ *      2              Missing Mandatory Parameter
+ *      3              Stale Cookie Error
+ *      4              Out of Resource
+ *      5              Unresolvable Address
+ *      6              Unrecognized Chunk Type
+ *      7              Invalid Mandatory Parameter
+ *      8              Unrecognized Parameters
+ *      9              No User Data
+ *     10              Cookie Received While Shutting Down
+ */
+typedef enum {
+
+	SCTP_ERROR_NO_ERROR	   = __constant_htons(0x00),
+	SCTP_ERROR_INV_STRM	   = __constant_htons(0x01),
+	SCTP_ERROR_MISS_PARAM 	   = __constant_htons(0x02),
+	SCTP_ERROR_STALE_COOKIE	   = __constant_htons(0x03),
+	SCTP_ERROR_NO_RESOURCE 	   = __constant_htons(0x04),
+	SCTP_ERROR_DNS_FAILED      = __constant_htons(0x05),
+	SCTP_ERROR_UNKNOWN_CHUNK   = __constant_htons(0x06),
+	SCTP_ERROR_INV_PARAM       = __constant_htons(0x07),
+	SCTP_ERROR_UNKNOWN_PARAM   = __constant_htons(0x08),
+	SCTP_ERROR_NO_DATA         = __constant_htons(0x09),
+	SCTP_ERROR_COOKIE_IN_SHUTDOWN = __constant_htons(0x0a),
+
+
+	/* SCTP Implementation Guide:
+	 *  11  Restart of an association with new addresses
+	 *  12  User Initiated Abort
+	 *  13  Protocol Violation
+	 */
+
+	SCTP_ERROR_RESTART         = __constant_htons(0x0b),
+	SCTP_ERROR_USER_ABORT      = __constant_htons(0x0c),
+	SCTP_ERROR_PROTO_VIOLATION = __constant_htons(0x0d),
+
+	/* ADDIP Section 3.3  New Error Causes
+	 *
+	 * Four new Error Causes are added to the SCTP Operational Errors,
+	 * primarily for use in the ASCONF-ACK chunk.
+	 *
+	 * Value          Cause Code
+	 * ---------      ----------------
+	 * 0x0100          Request to Delete Last Remaining IP Address.
+	 * 0x0101          Operation Refused Due to Resource Shortage.
+	 * 0x0102          Request to Delete Source IP Address.
+	 * 0x0103          Association Aborted due to illegal ASCONF-ACK
+	 */
+	SCTP_ERROR_DEL_LAST_IP	= __constant_htons(0x0100),
+	SCTP_ERROR_RSRC_LOW	= __constant_htons(0x0101),
+	SCTP_ERROR_DEL_SRC_IP	= __constant_htons(0x0102),
+	SCTP_ERROR_ASCONF_ACK   = __constant_htons(0x0103),
+
+} sctp_error_t;
+
+
+
+/* RFC 2960.  Appendix A.  Explicit Congestion Notification.
+ *   Explicit Congestion Notification Echo (ECNE) (12)
+ */
+typedef struct sctp_ecnehdr {
+	__u32 lowest_tsn;
+} sctp_ecnehdr_t;
+
+typedef struct sctp_ecne_chunk {
+	sctp_chunkhdr_t chunk_hdr;
+	sctp_ecnehdr_t ence_hdr;
+} sctp_ecne_chunk_t __attribute__((packed));
+
+/* RFC 2960.  Appendix A.  Explicit Congestion Notification.
+ *   Congestion Window Reduced (CWR) (13)
+ */
+typedef struct sctp_cwrhdr {
+	__u32 lowest_tsn;
+} sctp_cwrhdr_t;
+
+typedef struct sctp_cwr_chunk {
+	sctp_chunkhdr_t chunk_hdr;
+	sctp_cwrhdr_t cwr_hdr;
+} sctp_cwr_chunk_t __attribute__((packed));
+
+
+/* FIXME:  Cleanup needs to continue below this line. */
+
+/*
+ * ADDIP Section 3.1 New Chunk Types
+ */
+
+
+/* ADDIP Section 3.1.1
+ *
+ * ASCONF-Request Correlation ID: 32 bits (unsigned integer)
+ *
+ * This is an opaque integer assigned by the sender to identify each
+ * request parameter. It is in host byte order and is only meaningful
+ * to the sender. The receiver of the ASCONF Chunk will copy this 32
+ * bit value into the ASCONF Correlation ID field of the
+ * ASCONF-ACK. The sender of the ASCONF can use this same value in the
+ * ASCONF-ACK to find which request the response is for.
+ *
+ * ASCONF Parameter: TLV format
+ *
+ * Each Address configuration change is represented by a TLV parameter
+ * as defined in Section 3.2. One or more requests may be present in
+ * an ASCONF Chunk.
+ */
+typedef struct {
+	__u32	correlation;
+	sctp_paramhdr_t p;
+	__u8		payload[0];
+} sctpAsconfReq_t;
+
+/* ADDIP
+ * 3.1.1  Address/Stream Configuration Change Chunk (ASCONF)
+ *
+ * This chunk is used to communicate to the remote endpoint one of the
+ * configuration change requests that MUST be acknowledged.  The
+ * information carried in the ASCONF Chunk uses the form of a
+ * Tag-Length-Value (TLV), as described in "3.2.1
+ * Optional/Variable-length Parameter Format" in [RFC2960], for all
+ * variable parameters.
+ */
+typedef struct {
+	__u32	serial;
+	__u8	reserved[3];
+	__u8	addr_type;
+	__u32	addr[4];
+	sctpAsconfReq_t requests[0];
+} sctpAsconf_t;
+
+/* ADDIP
+ * 3.1.2 Address/Stream Configuration Acknowledgment Chunk (ASCONF-ACK)
+ *
+ * ASCONF-Request Correlation ID: 32 bits (unsigned integer)
+ *
+ * This value is copied from the ASCONF Correlation ID received in the
+ * ASCONF Chunk. It is used by the receiver of the ASCONF-ACK to identify
+ * which ASCONF parameter this response is associated with.
+ *
+ * ASCONF Parameter Response : TLV format
+ *
+ * The ASCONF Parameter Response is used in the ASCONF-ACK to report
+ * status of ASCONF processing. By default, if a responding endpoint
+ * does not include any Error Cause, a success is indicated. Thus a
+ * sender of an ASCONF-ACK MAY indicate complete success of all TLVs in
+ * an ASCONF by returning only the Chunk Type, Chunk Flags, Chunk Length
+ * (set to 8) and the Serial Number.
+ */
+typedef union {
+	struct {
+		__u32		correlation;
+		sctp_paramhdr_t header;	/* success report */
+	} success;
+	struct {
+		__u32		correlation;
+		sctp_paramhdr_t header;	/* error cause indication */
+		sctp_paramhdr_t errcause;
+		uint8_t request[0];	/* original request from ASCONF */
+	} error;
+#define __correlation	success.correlation
+#define __header	success.header
+#define __cause		error.errcause
+#define __request	error.request
+}  sctpAsconfAckRsp_t;
+
+/* ADDIP
+ * 3.1.2 Address/Stream Configuration Acknowledgment Chunk (ASCONF-ACK)
+ *
+ * This chunk is used by the receiver of an ASCONF Chunk to
+ * acknowledge the reception. It carries zero or more results for any
+ * ASCONF Parameters that were processed by the receiver.
+ */
+typedef struct {
+	__u32	serial;
+	sctpAsconfAckRsp_t responses[0];
+} sctpAsconfAck_t;
+
+/*********************************************************************
+ * Internal structures
+ *
+ * These are data structures which never go out on the wire.
+ *********************************************************************/
+
+/* What is this data structure for?  The TLV isn't one--it is just a
+ * value.  Perhaps this data structure ought to have a type--otherwise
+ * it is not unambigiously parseable.  --piggy
+ */
+typedef struct {
+	struct list_head hook;
+	int length;	/* length of the TLV */
+
+	/* the actually TLV to be copied into ASCONF_ACK */
+	sctpAsconfAckRsp_t TLV;
+} sctpAsconfAckRspNode_t;
+
+#endif /* __LINUX_SCTP_H__ */
diff -urN linux-2.4.23-pre5/include/linux/socket.h linux-2.4.23-pre6/include/linux/socket.h
--- linux-2.4.23-pre5/include/linux/socket.h	2003-06-13 07:51:39.000000000 -0700
+++ linux-2.4.23-pre6/include/linux/socket.h	2003-10-01 10:38:57.000000000 -0700
@@ -242,6 +242,7 @@
 #define SOL_UDP		17
 #define SOL_IPV6	41
 #define SOL_ICMPV6	58
+#define SOL_SCTP	132
 #define SOL_RAW		255
 #define SOL_IPX		256
 #define SOL_AX25	257
diff -urN linux-2.4.23-pre5/include/linux/sysctl.h linux-2.4.23-pre6/include/linux/sysctl.h
--- linux-2.4.23-pre5/include/linux/sysctl.h	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/include/linux/sysctl.h	2003-10-01 10:38:57.000000000 -0700
@@ -176,7 +176,8 @@
 	NET_TR=14,
 	NET_DECNET=15,
 	NET_ECONET=16,
-	NET_KHTTPD=17
+	NET_KHTTPD=17,
+	NET_SCTP=18
 };
 
 /* /proc/sys/kernel/random */
@@ -509,6 +510,21 @@
 	NET_DECNET_DEBUG_LEVEL = 255
 };
 
+/* /proc/sys/net/sctp */
+enum {
+	NET_SCTP_RTO_INITIAL = 1,
+	NET_SCTP_RTO_MIN     = 2,
+	NET_SCTP_RTO_MAX     = 3,
+	NET_SCTP_RTO_ALPHA   = 4,
+	NET_SCTP_RTO_BETA    = 5,
+	NET_SCTP_VALID_COOKIE_LIFE       =  6,
+	NET_SCTP_ASSOCIATION_MAX_RETRANS =  7,
+	NET_SCTP_PATH_MAX_RETRANS        =  8,
+	NET_SCTP_MAX_INIT_RETRANSMITS    =  9,
+	NET_SCTP_HB_INTERVAL             = 10,
+	NET_SCTP_PRESERVE_ENABLE         = 11,
+	NET_SCTP_MAX_BURST               = 12,
+};
 /* /proc/sys/net/khttpd/ */
 enum {
 	NET_KHTTPD_DOCROOT	= 1,
diff -urN linux-2.4.23-pre5/include/net/if_inet6.h linux-2.4.23-pre6/include/net/if_inet6.h
--- linux-2.4.23-pre5/include/net/if_inet6.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/net/if_inet6.h	2003-10-01 10:38:57.000000000 -0700
@@ -15,6 +15,10 @@
 #ifndef _NET_IF_INET6_H
 #define _NET_IF_INET6_H
 
+#include <linux/ipv6.h>
+
+#define IF_RA_OTHERCONF	0x80
+#define IF_RA_MANAGED	0x40
 #define IF_RA_RCVD	0x20
 #define IF_RS_SENT	0x10
 
@@ -124,22 +128,6 @@
 #define	IFA_SITE	IPV6_ADDR_SITELOCAL
 #define	IFA_GLOBAL	0x0000U
 
-struct ipv6_devconf
-{
-	int		forwarding;
-	int		hop_limit;
-	int		mtu6;
-	int		accept_ra;
-	int		accept_redirects;
-	int		autoconf;
-	int		dad_transmits;
-	int		rtr_solicits;
-	int		rtr_solicit_interval;
-	int		rtr_solicit_delay;
-
-	void		*sysctl;
-};
-
 struct inet6_dev 
 {
 	struct net_device		*dev;
diff -urN linux-2.4.23-pre5/include/net/inet_common.h linux-2.4.23-pre6/include/net/inet_common.h
--- linux-2.4.23-pre5/include/net/inet_common.h	1999-08-23 10:01:02.000000000 -0700
+++ linux-2.4.23-pre6/include/net/inet_common.h	2003-10-01 10:38:57.000000000 -0700
@@ -43,6 +43,14 @@
 extern void			inet_sock_destruct(struct sock *sk);
 extern atomic_t			inet_sock_nr;
 
+extern int			inet_bind(struct socket *sock,
+					  struct sockaddr *uaddr, int addr_len);
+extern int			inet_getname(struct socket *sock,
+					     struct sockaddr *uaddr,
+					     int *uaddr_len, int peer);
+extern int			inet_ioctl(struct socket *sock,
+					   unsigned int cmd, unsigned long arg);
+
 #endif
 
 
diff -urN linux-2.4.23-pre5/include/net/ip.h linux-2.4.23-pre6/include/net/ip.h
--- linux-2.4.23-pre5/include/net/ip.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/net/ip.h	2003-10-01 10:38:57.000000000 -0700
@@ -96,7 +96,7 @@
 extern int		ip_fragment(struct sk_buff *skb, int (*out)(struct sk_buff*));
 extern int		ip_do_nat(struct sk_buff *skb);
 extern void		ip_send_check(struct iphdr *ip);
-extern int		ip_queue_xmit(struct sk_buff *skb);
+extern int		ip_queue_xmit(struct sk_buff *skb, int ipfragok);
 extern void		ip_init(void);
 extern int		ip_build_xmit(struct sock *sk,
 				      int getfrag (const void *,
diff -urN linux-2.4.23-pre5/include/net/ipv6.h linux-2.4.23-pre6/include/net/ipv6.h
--- linux-2.4.23-pre5/include/net/ipv6.h	2003-06-13 07:51:39.000000000 -0700
+++ linux-2.4.23-pre6/include/net/ipv6.h	2003-10-01 10:38:57.000000000 -0700
@@ -353,6 +353,14 @@
 						u32 info, u8 *payload);
 extern void			ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info);
 
+extern int inet6_release(struct socket *sock);
+extern int inet6_bind(struct socket *sock, struct sockaddr *uaddr, 
+		      int addr_len);
+extern int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
+			 int *uaddr_len, int peer);
+extern int inet6_ioctl(struct socket *sock, unsigned int cmd, 
+		       unsigned long arg);
+
 #endif /* __KERNEL__ */
 #endif /* _NET_IPV6_H */
 
diff -urN linux-2.4.23-pre5/include/net/sctp/command.h linux-2.4.23-pre6/include/net/sctp/command.h
--- linux-2.4.23-pre5/include/net/sctp/command.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/include/net/sctp/command.h	2003-10-01 10:38:57.000000000 -0700
@@ -0,0 +1,214 @@
+/* SCTP kernel reference Implementation Copyright (C) 1999-2001
+ * Cisco, Motorola, and IBM
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * These are the definitions needed for the command object.
+ *
+ * The SCTP reference implementation  is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * the SCTP reference implementation  is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to one of the
+ * following email addresses:
+ *
+ * La Monte H.P. Yarroll <piggy@acm.org>
+ * Karl Knutson <karl@athena.chicago.il.us>
+ * Ardelle Fan <ardelle.fan@intel.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+
+#ifndef __net_sctp_command_h__
+#define __net_sctp_command_h__
+
+#include <net/sctp/constants.h>
+#include <net/sctp/structs.h>
+
+
+typedef enum {
+	SCTP_CMD_NOP = 0,	/* Do nothing. */
+	SCTP_CMD_NEW_ASOC,	/* Register a new association.  */
+	SCTP_CMD_DELETE_TCB,	/* Delete the current association. */
+	SCTP_CMD_NEW_STATE,	/* Enter a new state.  */
+	SCTP_CMD_REPORT_TSN,	/* Record the arrival of a TSN.  */
+	SCTP_CMD_GEN_SACK,	/* Send a Selective ACK (maybe).  */
+	SCTP_CMD_PROCESS_SACK,	/* Process an inbound SACK.  */
+	SCTP_CMD_GEN_INIT_ACK,	/* Generate an INIT ACK chunk.  */
+	SCTP_CMD_PEER_INIT,	/* Process a INIT from the peer.  */
+	SCTP_CMD_GEN_COOKIE_ECHO, /* Generate a COOKIE ECHO chunk. */
+	SCTP_CMD_CHUNK_ULP,	/* Send a chunk to the sockets layer.  */
+	SCTP_CMD_EVENT_ULP,	/* Send a notification to the sockets layer. */
+	SCTP_CMD_REPLY,		/* Send a chunk to our peer.  */
+	SCTP_CMD_SEND_PKT,	/* Send a full packet to our peer.  */
+	SCTP_CMD_RETRAN,	/* Mark a transport for retransmission.  */
+	SCTP_CMD_ECN_CE,        /* Do delayed CE processing.   */
+	SCTP_CMD_ECN_ECNE,	/* Do delayed ECNE processing. */
+	SCTP_CMD_ECN_CWR,	/* Do delayed CWR processing.  */
+	SCTP_CMD_TIMER_START,	/* Start a timer.  */
+	SCTP_CMD_TIMER_RESTART,	/* Restart a timer. */
+	SCTP_CMD_TIMER_STOP,	/* Stop a timer. */
+	SCTP_CMD_COUNTER_RESET, /* Reset a counter. */
+	SCTP_CMD_COUNTER_INC,   /* Increment a counter. */
+	SCTP_CMD_INIT_RESTART,  /* High level, do init timer work. */
+	SCTP_CMD_INIT_FAILED,   /* High level, do init failure work. */
+	SCTP_CMD_REPORT_DUP,	/* Report a duplicate TSN.  */
+	SCTP_CMD_STRIKE,	/* Mark a strike against a transport.  */
+	SCTP_CMD_TRANSMIT,      /* Transmit the outqueue. */
+	SCTP_CMD_HB_TIMERS_START,    /* Start the heartbeat timers. */
+	SCTP_CMD_HB_TIMER_UPDATE,    /* Update a heartbeat timers.  */
+	SCTP_CMD_HB_TIMERS_STOP,     /* Stop the heartbeat timers.  */
+	SCTP_CMD_TRANSPORT_RESET,    /* Reset the status of a transport. */
+	SCTP_CMD_TRANSPORT_ON,       /* Mark the transport as active. */
+	SCTP_CMD_REPORT_ERROR,   /* Pass this error back out of the sm. */
+	SCTP_CMD_REPORT_BAD_TAG, /* Verification tags didn't match. */
+	SCTP_CMD_PROCESS_CTSN,   /* Sideeffect from shutdown. */
+	SCTP_CMD_ASSOC_FAILED,	 /* Handle association failure. */
+	SCTP_CMD_DISCARD_PACKET, /* Discard the whole packet. */
+	SCTP_CMD_GEN_SHUTDOWN,   /* Generate a SHUTDOWN chunk. */
+	SCTP_CMD_UPDATE_ASSOC,   /* Update association information. */
+	SCTP_CMD_PURGE_OUTQUEUE, /* Purge all data waiting to be sent. */
+	SCTP_CMD_SETUP_T2,       /* Hi-level, setup T2-shutdown parms.  */
+	SCTP_CMD_RTO_PENDING,	 /* Set transport's rto_pending. */
+	SCTP_CMD_PART_DELIVER,	 /* Partial data delivery considerations. */
+	SCTP_CMD_RENEGE,         /* Renege data on an association. */
+	SCTP_CMD_LAST
+} sctp_verb_t;
+
+#define SCTP_CMD_MAX		(SCTP_CMD_LAST - 1)
+#define SCTP_CMD_NUM_VERBS	(SCTP_CMD_MAX + 1)
+
+/* How many commands can you put in an sctp_cmd_seq_t?
+ * This is a rather arbitrary number, ideally derived from a careful
+ * analysis of the state functions, but in reality just taken from
+ * thin air in the hopes othat we don't trigger a kernel panic.
+ */
+#define SCTP_MAX_NUM_COMMANDS 14
+
+typedef union {
+	__s32 i32;
+	__u32 u32;
+	__u16 u16;
+	__u8 u8;
+	int error;
+	sctp_state_t state;
+	sctp_event_timeout_t to;
+	sctp_counter_t counter;
+	void *ptr;
+	struct sctp_chunk *chunk;
+	struct sctp_association *asoc;
+	struct sctp_transport *transport;
+	struct sctp_bind_addr *bp;
+	sctp_init_chunk_t *init;
+	struct sctp_ulpevent *ulpevent;
+	struct sctp_packet *packet;
+	sctp_sackhdr_t *sackh;
+} sctp_arg_t;
+
+/* We are simulating ML type constructors here.
+ *
+ * SCTP_ARG_CONSTRUCTOR(NAME, TYPE, ELT) builds a function called
+ * SCTP_NAME() which takes an argument of type TYPE and returns an
+ * sctp_arg_t.  It does this by inserting the sole argument into the
+ * ELT union element of a local sctp_arg_t.
+ *
+ * E.g., SCTP_ARG_CONSTRUCTOR(I32, __s32, i32) builds SCTP_I32(arg),
+ * which takes an __s32 and returns a sctp_arg_t containing the
+ * __s32.  So, after foo = SCTP_I32(arg), foo.i32 == arg.
+ */
+static inline sctp_arg_t SCTP_NULL(void)
+{
+	sctp_arg_t retval; retval.ptr = NULL; return retval;
+}
+static inline sctp_arg_t SCTP_NOFORCE(void)
+{
+	sctp_arg_t retval; retval.i32 = 0; return retval;
+}
+static inline sctp_arg_t SCTP_FORCE(void)
+{
+	sctp_arg_t retval; retval.i32 = 1; return retval;
+}
+
+#define SCTP_ARG_CONSTRUCTOR(name, type, elt) \
+static inline sctp_arg_t	\
+SCTP_## name (type arg)		\
+{ sctp_arg_t retval; retval.elt = arg; return retval; }
+
+SCTP_ARG_CONSTRUCTOR(I32,	__s32, i32)
+SCTP_ARG_CONSTRUCTOR(U32,	__u32, u32)
+SCTP_ARG_CONSTRUCTOR(U16,	__u16, u16)
+SCTP_ARG_CONSTRUCTOR(U8,	__u8, u8)
+SCTP_ARG_CONSTRUCTOR(ERROR,     int, error)
+SCTP_ARG_CONSTRUCTOR(STATE,	sctp_state_t, state)
+SCTP_ARG_CONSTRUCTOR(COUNTER,	sctp_counter_t, counter)
+SCTP_ARG_CONSTRUCTOR(TO,	sctp_event_timeout_t, to)
+SCTP_ARG_CONSTRUCTOR(PTR,	void *, ptr)
+SCTP_ARG_CONSTRUCTOR(CHUNK,	struct sctp_chunk *, chunk)
+SCTP_ARG_CONSTRUCTOR(ASOC,	struct sctp_association *, asoc)
+SCTP_ARG_CONSTRUCTOR(TRANSPORT,	struct sctp_transport *, transport)
+SCTP_ARG_CONSTRUCTOR(BA,	struct sctp_bind_addr *, bp)
+SCTP_ARG_CONSTRUCTOR(PEER_INIT,	sctp_init_chunk_t *, init)
+SCTP_ARG_CONSTRUCTOR(ULPEVENT,  struct sctp_ulpevent *, ulpevent)
+SCTP_ARG_CONSTRUCTOR(PACKET,	struct sctp_packet *, packet)
+SCTP_ARG_CONSTRUCTOR(SACKH,	sctp_sackhdr_t *, sackh)
+
+typedef struct {
+	sctp_arg_t obj;
+	sctp_verb_t verb;
+} sctp_cmd_t;
+
+typedef struct {
+	sctp_cmd_t cmds[SCTP_MAX_NUM_COMMANDS];
+	__u8 next_free_slot;
+	__u8 next_cmd;
+} sctp_cmd_seq_t;
+
+
+/* Create a new sctp_command_sequence.
+ * Return NULL if creating a new sequence fails.
+ */
+sctp_cmd_seq_t *sctp_new_cmd_seq(int gfp);
+
+/* Initialize a block of memory as a command sequence.
+ * Return 0 if the initialization fails.
+ */
+int sctp_init_cmd_seq(sctp_cmd_seq_t *seq);
+
+/* Add a command to an sctp_cmd_seq_t.
+ * Return 0 if the command sequence is full.
+ *
+ * Use the SCTP_* constructors defined by SCTP_ARG_CONSTRUCTOR() above
+ * to wrap data which goes in the obj argument.
+ */
+int sctp_add_cmd(sctp_cmd_seq_t *seq, sctp_verb_t verb, sctp_arg_t obj);
+
+/* Rewind an sctp_cmd_seq_t to iterate from the start.
+ * Return 0 if the rewind fails.
+ */
+int sctp_rewind_sequence(sctp_cmd_seq_t *seq);
+
+/* Return the next command structure in an sctp_cmd_seq.
+ * Return NULL at the end of the sequence.
+ */
+sctp_cmd_t *sctp_next_cmd(sctp_cmd_seq_t *seq);
+
+/* Dispose of a command sequence.  */
+void sctp_free_cmd_seq(sctp_cmd_seq_t *seq);
+
+#endif /* __net_sctp_command_h__ */
+
diff -urN linux-2.4.23-pre5/include/net/sctp/compat.h linux-2.4.23-pre6/include/net/sctp/compat.h
--- linux-2.4.23-pre5/include/net/sctp/compat.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/include/net/sctp/compat.h	2003-10-01 10:38:57.000000000 -0700
@@ -0,0 +1,102 @@
+/* SCTP kernel reference Implementation
+ *
+ * Copyright (c) 2003 Hewlett-Packard Company
+ * 
+ * This file is part of the SCTP kernel reference Implementation
+ * 
+ * This header represents the structures and constants needed to backport
+ * lksctp from Linux kernel 2.5 to 2.4 This file also has some code that
+ * has been taken from the source base of Linux kernel 2.5 
+ * 
+ * The SCTP reference implementation is free software; 
+ * you can redistribute it and/or modify it under the terms of 
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ * 
+ * The SCTP reference implementation is distributed in the hope that it 
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.  
+ * 
+ */
+
+#ifndef __net_sctp_compat_h__
+#define __net_sctp_compat_h__
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/seq_file.h>
+
+/*
+ * The following defines are for compatibility with 2.5
+ */
+/*
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr:	the pointer to the member.
+ * @type:	the type of the container struct this is embedded in.
+ * @member:	the name of the member within the struct.
+ */
+#define container_of(ptr, type, member) ({			\
+	const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
+	(type *)( (char *)__mptr - offsetof(type,member) );})
+
+#define DEFINE_SNMP_STAT(type, name)	\
+	type name[NR_CPUS * 2]
+#define DECLARE_SNMP_STAT(type, name)	\
+	extern type name[]
+#define SNMP_DEC_STATS(mib, field) ((mib)[2*smp_processor_id()+!in_softirq()].field--)
+
+#define sctp_sk(__sk) (&(((struct sock *)__sk)->tp_pinfo.af_sctp))
+#define inet_sk(__sk) (&(((struct sock *)__sk)->protinfo.af_inet))
+#define inet6_sk(__sk) (&(((struct sock *)__sk)->net_pinfo.af_inet6))
+
+#define virt_addr_valid(x)	VALID_PAGE(virt_to_page((x)))
+#define sock_owned_by_user(sk)  ((sk)->lock.users)
+#define sk_set_owner(x, y)
+#define __unsafe(x) MOD_INC_USE_COUNT
+#define dst_pmtu(x) ((x)->pmtu)
+
+void sctp_hash_digest(const char *key, const int in_key_len,
+		      const char *text, const int text_len,
+		      __u8 *digest);
+/*
+ * find last bit set.
+ */
+static __inline__ int fls(int x)
+{
+	int r = 32;
+	
+	if (!x)
+		return 0;
+	if (!(x & 0xffff0000u)) {
+		x <<= 16;
+		r -= 16;
+	}
+	if (!(x & 0xff000000u)) {
+		x <<= 8;
+		r -= 8;
+	}
+	if (!(x & 0xf0000000u)) {
+		x <<= 4;
+		r -= 4;
+	}
+	if (!(x & 0xc0000000u)) {
+		x <<= 2;
+		r -= 2;
+	}
+	if (!(x & 0x80000000u)) {
+		x <<= 1;
+		r -= 1;
+	}
+	return r;
+}
+
+#endif /* __net_sctp_compat_h__ */
diff -urN linux-2.4.23-pre5/include/net/sctp/constants.h linux-2.4.23-pre6/include/net/sctp/constants.h
--- linux-2.4.23-pre5/include/net/sctp/constants.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/include/net/sctp/constants.h	2003-10-01 10:38:57.000000000 -0700
@@ -0,0 +1,439 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001 Intel Corp.
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *   La Monte H.P. Yarroll <piggy@acm.org>
+ *   Karl Knutson          <karl@athena.chicago.il.us>
+ *   Randall Stewart       <randall@stewart.chicago.il.us>
+ *   Ken Morneau           <kmorneau@cisco.com>
+ *   Qiaobing Xie          <qxie1@motorola.com>
+ *   Xingang Guo           <xingang.guo@intel.com>
+ *   Sridhar Samudrala     <samudrala@us.ibm.com>
+ *   Daisy Chang           <daisyc@us.ibm.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#ifndef __sctp_constants_h__
+#define __sctp_constants_h__
+
+#include <linux/tcp.h>  /* For TCP states used in sctp_sock_state_t */
+#include <linux/sctp.h>
+#include <linux/ipv6.h> /* For ipv6hdr. */
+#include <net/sctp/user.h>
+
+/* Value used for stream negotiation. */
+enum { SCTP_MAX_STREAM = 0xffff };
+enum { SCTP_DEFAULT_OUTSTREAMS = 10 };
+enum { SCTP_DEFAULT_INSTREAMS = SCTP_MAX_STREAM };
+
+/* Define the amount of space to reserve for SCTP, IP, LL.
+ * There is a little bit of waste that we are always allocating
+ * for ipv6 headers, but this seems worth the simplicity.
+ */
+
+#define SCTP_IP_OVERHEAD ((sizeof(struct sctphdr)\
+                          + sizeof(struct ipv6hdr)\
+                          + MAX_HEADER))
+
+/* Since CIDs are sparse, we need all four of the following
+ * symbols.  CIDs are dense through SCTP_CID_BASE_MAX.
+ */
+#define SCTP_CID_BASE_MAX		SCTP_CID_SHUTDOWN_COMPLETE
+#define SCTP_CID_MAX			SCTP_CID_ASCONF_ACK
+
+#define SCTP_NUM_BASE_CHUNK_TYPES	(SCTP_CID_BASE_MAX + 1)
+#define SCTP_NUM_CHUNK_TYPES		(SCTP_NUM_BASE_CHUNKTYPES + 2)
+
+
+/* These are the different flavours of event.  */
+typedef enum {
+
+	SCTP_EVENT_T_CHUNK = 1,
+	SCTP_EVENT_T_TIMEOUT,
+	SCTP_EVENT_T_OTHER,
+	SCTP_EVENT_T_PRIMITIVE
+
+} sctp_event_t;
+
+#define SCTP_EVENT_T_MAX SCTP_EVENT_T_PRIMITIVE
+#define SCTP_EVENT_T_NUM (SCTP_EVENT_T_MAX + 1)
+
+/* As a convenience for the state machine, we append SCTP_EVENT_* and
+ * SCTP_ULP_* to the list of possible chunks.
+ */
+
+typedef enum {
+	SCTP_EVENT_TIMEOUT_NONE = 0,
+	SCTP_EVENT_TIMEOUT_T1_COOKIE,
+	SCTP_EVENT_TIMEOUT_T1_INIT,
+	SCTP_EVENT_TIMEOUT_T2_SHUTDOWN,
+	SCTP_EVENT_TIMEOUT_T3_RTX,
+	SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD,
+	SCTP_EVENT_TIMEOUT_HEARTBEAT,
+	SCTP_EVENT_TIMEOUT_SACK,
+	SCTP_EVENT_TIMEOUT_AUTOCLOSE,
+} sctp_event_timeout_t;
+
+#define SCTP_EVENT_TIMEOUT_MAX		SCTP_EVENT_TIMEOUT_AUTOCLOSE
+#define SCTP_NUM_TIMEOUT_TYPES		(SCTP_EVENT_TIMEOUT_MAX + 1)
+
+typedef enum {
+	SCTP_EVENT_NO_PENDING_TSN = 0,
+} sctp_event_other_t;
+
+#define SCTP_EVENT_OTHER_MAX		SCTP_EVENT_NO_PENDING_TSN
+#define SCTP_NUM_OTHER_TYPES		(SCTP_EVENT_OTHER_MAX + 1)
+
+/* These are primitive requests from the ULP.  */
+typedef enum {
+	SCTP_PRIMITIVE_ASSOCIATE = 0,
+	SCTP_PRIMITIVE_SHUTDOWN,
+	SCTP_PRIMITIVE_ABORT,
+	SCTP_PRIMITIVE_SEND,
+	SCTP_PRIMITIVE_REQUESTHEARTBEAT,
+} sctp_event_primitive_t;
+
+#define SCTP_EVENT_PRIMITIVE_MAX	SCTP_PRIMITIVE_REQUESTHEARTBEAT
+#define SCTP_NUM_PRIMITIVE_TYPES	(SCTP_EVENT_PRIMITIVE_MAX + 1)
+
+/* We define here a utility type for manipulating subtypes.
+ * The subtype constructors all work like this:
+ *
+ * 	sctp_subtype_t foo = SCTP_ST_CHUNK(SCTP_CID_INIT);
+ */
+
+typedef union {
+	sctp_cid_t chunk;
+	sctp_event_timeout_t timeout;
+	sctp_event_other_t other;
+	sctp_event_primitive_t primitive;
+} sctp_subtype_t;
+
+#define SCTP_SUBTYPE_CONSTRUCTOR(_name, _type, _elt) \
+static inline sctp_subtype_t	\
+SCTP_ST_## _name (_type _arg)		\
+{ sctp_subtype_t _retval; _retval._elt = _arg; return _retval; }
+
+SCTP_SUBTYPE_CONSTRUCTOR(CHUNK,		sctp_cid_t,		chunk)
+SCTP_SUBTYPE_CONSTRUCTOR(TIMEOUT,	sctp_event_timeout_t,	timeout)
+SCTP_SUBTYPE_CONSTRUCTOR(OTHER,		sctp_event_other_t,	other)
+SCTP_SUBTYPE_CONSTRUCTOR(PRIMITIVE,	sctp_event_primitive_t,	primitive)
+
+
+#define sctp_chunk_is_control(a) (a->chunk_hdr->type != SCTP_CID_DATA)
+#define sctp_chunk_is_data(a) (a->chunk_hdr->type == SCTP_CID_DATA)
+
+/* Calculate the actual data size in a data chunk */
+#define SCTP_DATA_SNDSIZE(c) ((int)((unsigned long)(c->chunk_end)\
+		       		- (unsigned long)(c->chunk_hdr)\
+				- sizeof(sctp_data_chunk_t)))
+
+/* This is a table of printable names of sctp_param_t's.  */
+extern const char *sctp_param_tbl[];
+
+
+#define SCTP_MAX_ERROR_CAUSE  SCTP_ERROR_NONEXIST_IP
+#define SCTP_NUM_ERROR_CAUSE  10
+
+/* Internal error codes */
+typedef enum {
+
+	SCTP_IERROR_NO_ERROR	        = 0,
+	SCTP_IERROR_BASE		= 1000,
+	SCTP_IERROR_NO_COOKIE,
+	SCTP_IERROR_BAD_SIG,
+	SCTP_IERROR_STALE_COOKIE,
+	SCTP_IERROR_NOMEM,
+	SCTP_IERROR_MALFORMED,
+	SCTP_IERROR_BAD_TAG,
+	SCTP_IERROR_BIG_GAP,
+	SCTP_IERROR_DUP_TSN,
+
+} sctp_ierror_t;
+
+
+
+/* SCTP state defines for internal state machine */
+typedef enum {
+
+	SCTP_STATE_EMPTY		= 0,
+	SCTP_STATE_CLOSED		= 1,
+	SCTP_STATE_COOKIE_WAIT		= 2,
+	SCTP_STATE_COOKIE_ECHOED	= 3,
+	SCTP_STATE_ESTABLISHED		= 4,
+	SCTP_STATE_SHUTDOWN_PENDING	= 5,
+	SCTP_STATE_SHUTDOWN_SENT	= 6,
+	SCTP_STATE_SHUTDOWN_RECEIVED	= 7,
+	SCTP_STATE_SHUTDOWN_ACK_SENT	= 8,
+
+} sctp_state_t;
+
+#define SCTP_STATE_MAX			SCTP_STATE_SHUTDOWN_ACK_SENT
+#define SCTP_STATE_NUM_STATES		(SCTP_STATE_MAX + 1)
+
+/* These are values for sk->state.
+ * For a UDP-style SCTP socket, the states are defined as follows
+ * - A socket in SCTP_SS_CLOSED state indicates that it is not willing to
+ *   accept new associations, but it can initiate the creation of new ones.
+ * - A socket in SCTP_SS_LISTENING state indicates that it is willing to
+ *   accept new  associations and can initiate the creation of new ones.
+ * - A socket in SCTP_SS_ESTABLISHED state indicates that it is a peeled off
+ *   socket with one association.
+ * For a TCP-style SCTP socket, the states are defined as follows
+ * - A socket in SCTP_SS_CLOSED state indicates that it is not willing to
+ *   accept new associations, but it can initiate the creation of new ones.
+ * - A socket in SCTP_SS_LISTENING state indicates that it is willing to
+ *   accept new associations, but cannot initiate the creation of new ones.
+ * - A socket in SCTP_SS_ESTABLISHED state indicates that it has a single 
+ *   association.
+ */
+typedef enum {
+	SCTP_SS_CLOSED         = TCP_CLOSE,
+	SCTP_SS_LISTENING      = TCP_LISTEN,
+	SCTP_SS_ESTABLISHING   = TCP_SYN_SENT,
+	SCTP_SS_ESTABLISHED    = TCP_ESTABLISHED,
+	SCTP_SS_DISCONNECTING  = TCP_CLOSING,
+} sctp_sock_state_t;
+
+/* These functions map various type to printable names.  */
+const char *sctp_cname(const sctp_subtype_t);	/* chunk types */
+const char *sctp_oname(const sctp_subtype_t);	/* other events */
+const char *sctp_tname(const sctp_subtype_t);	/* timeouts */
+const char *sctp_pname(const sctp_subtype_t);	/* primitives */
+
+/* This is a table of printable names of sctp_state_t's.  */
+extern const char *sctp_state_tbl[], *sctp_evttype_tbl[], *sctp_status_tbl[];
+
+/* SCTP reachability state for each address */
+#define SCTP_ADDR_NOHB			4
+#define SCTP_ADDR_REACHABLE		2
+#define SCTP_ADDR_NOT_REACHABLE		1
+
+/* Maximum chunk length considering padding requirements. */
+enum { SCTP_MAX_CHUNK_LEN = ((1<<16) - sizeof(__u32)) };
+
+/* Encourage Cookie-Echo bundling by pre-fragmenting chunks a little
+ * harder (until reaching ESTABLISHED state).
+ */
+enum { SCTP_ARBITRARY_COOKIE_ECHO_LEN = 200 };
+
+/* Guess at how big to make the TSN mapping array.
+ * We guarantee that we can handle at least this big a gap between the
+ * cumulative ACK and the highest TSN.  In practice, we can often
+ * handle up to twice this value.
+ *
+ * NEVER make this more than 32767 (2^15-1).  The Gap Ack Blocks in a
+ * SACK (see  section 3.3.4) are only 16 bits, so 2*SCTP_TSN_MAP_SIZE
+ * must be less than 65535 (2^16 - 1), or we will have overflow
+ * problems creating SACK's.
+ */
+#define SCTP_TSN_MAP_SIZE 2048
+#define SCTP_TSN_MAX_GAP  65535
+
+/* We will not record more than this many duplicate TSNs between two
+ * SACKs.  The minimum PMTU is 576.  Remove all the headers and there
+ * is enough room for 131 duplicate reports.  Round down to the
+ * nearest power of 2.
+ */
+enum { SCTP_MIN_PMTU = 576 };
+enum { SCTP_MAX_DUP_TSNS = 128 };
+
+typedef enum {
+	SCTP_COUNTER_INIT_ERROR,
+} sctp_counter_t;
+
+/* How many counters does an association need? */
+#define SCTP_NUMBER_COUNTERS	5
+
+/* Here we define the default timers.  */
+
+/* cookie timer def = ? seconds */
+#define SCTP_DEFAULT_TIMEOUT_T1_COOKIE	(3 * HZ)
+
+/* init timer def = 3 seconds  */
+#define SCTP_DEFAULT_TIMEOUT_T1_INIT	(3 * HZ)
+
+/* shutdown timer def = 300 ms */
+#define SCTP_DEFAULT_TIMEOUT_T2_SHUTDOWN ((300 * HZ) / 1000)
+
+/* 0 seconds + RTO */
+#define SCTP_DEFAULT_TIMEOUT_HEARTBEAT	(10 * HZ)
+
+/* recv timer def = 200ms (in usec) */
+#define SCTP_DEFAULT_TIMEOUT_SACK	((200 * HZ) / 1000)
+#define SCTP_DEFAULT_TIMEOUT_SACK_MAX	((500 * HZ) / 1000) /* 500 ms */
+
+/* RTO.Initial              - 3  seconds
+ * RTO.Min                  - 1  second
+ * RTO.Max                  - 60 seconds
+ * RTO.Alpha                - 1/8
+ * RTO.Beta                 - 1/4
+ */
+#define SCTP_RTO_INITIAL	(3 * HZ)
+#define SCTP_RTO_MIN		(1 * HZ)
+#define SCTP_RTO_MAX		(60 * HZ)
+
+#define SCTP_RTO_ALPHA          3   /* 1/8 when converted to right shifts. */
+#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_CLOCK_GRANULARITY	1	/* 1 jiffy */
+
+#define SCTP_DEF_MAX_INIT 6
+#define SCTP_DEF_MAX_SEND 10
+
+#define SCTP_DEFAULT_COOKIE_LIFE_SEC	60 /* seconds */
+#define SCTP_DEFAULT_COOKIE_LIFE_USEC	0  /* microseconds */
+
+#define SCTP_DEFAULT_MINWINDOW	1500	/* default minimum rwnd size */
+#define SCTP_DEFAULT_MAXWINDOW	32768	/* default rwnd size */
+#define SCTP_DEFAULT_MAXSEGMENT 1500	/* MTU size, this is the limit
+                                         * to which we will raise the P-MTU.
+					 */
+#define SCTP_DEFAULT_MINSEGMENT 512	/* MTU size ... if no mtu disc */
+#define SCTP_HOW_MANY_SECRETS 2		/* How many secrets I keep */
+#define SCTP_HOW_LONG_COOKIE_LIVE 3600	/* How many seconds the current
+					 * secret will live?
+					 */
+#define SCTP_SECRET_SIZE 32		/* Number of octets in a 256 bits. */
+
+#define SCTP_SIGNATURE_SIZE 20	        /* size of a SLA-1 signature */
+
+#define SCTP_COOKIE_MULTIPLE 32 /* Pad out our cookie to make our hash
+				 * functions simpler to write.
+				 */
+
+#if defined (CONFIG_SCTP_HMAC_MD5)
+#define SCTP_COOKIE_HMAC_ALG "md5"
+#elif defined (CONFIG_SCTP_HMAC_SHA1)
+#define SCTP_COOKIE_HMAC_ALG "sha1"
+#else
+#define SCTP_COOKIE_HMAC_ALG NULL
+#endif
+
+/* These return values describe the success or failure of a number of
+ * routines which form the lower interface to SCTP_outqueue.
+ */
+typedef enum {
+	SCTP_XMIT_OK,
+	SCTP_XMIT_PMTU_FULL,
+	SCTP_XMIT_RWND_FULL,
+	SCTP_XMIT_MUST_FRAG,
+	SCTP_XMIT_NAGLE_DELAY,
+} sctp_xmit_t;
+
+/* These are the commands for manipulating transports.  */
+typedef enum {
+	SCTP_TRANSPORT_UP,
+	SCTP_TRANSPORT_DOWN,
+} sctp_transport_cmd_t;
+
+/* These are the address scopes defined mainly for IPv4 addresses
+ * based on draft of SCTP IPv4 scoping <draft-stewart-tsvwg-sctp-ipv4-00.txt>.
+ * These scopes are hopefully generic enough to be used on scoping both
+ * IPv4 and IPv6 addresses in SCTP.
+ * At this point, the IPv6 scopes will be mapped to these internal scopes
+ * as much as possible.
+ */
+typedef enum {
+	SCTP_SCOPE_GLOBAL,		/* IPv4 global addresses */
+	SCTP_SCOPE_PRIVATE,		/* IPv4 private addresses */
+	SCTP_SCOPE_LINK,		/* IPv4 link local address */
+	SCTP_SCOPE_LOOPBACK,		/* IPv4 loopback address */
+	SCTP_SCOPE_UNUSABLE,		/* IPv4 unusable addresses */
+} sctp_scope_t;
+
+/* Based on IPv4 scoping <draft-stewart-tsvwg-sctp-ipv4-00.txt>,
+ * SCTP IPv4 unusable addresses: 0.0.0.0/8, 224.0.0.0/4, 198.18.0.0/24,
+ * 192.88.99.0/24.
+ * Also, RFC 8.4, non-unicast addresses are not considered valid SCTP
+ * addresses.
+ */
+#define IS_IPV4_UNUSABLE_ADDRESS(a) \
+	((INADDR_BROADCAST == *a) || \
+	(MULTICAST(*a)) || \
+	(((unsigned char *)(a))[0] == 0) || \
+	((((unsigned char *)(a))[0] == 198) && \
+	(((unsigned char *)(a))[1] == 18) && \
+	(((unsigned char *)(a))[2] == 0)) || \
+	((((unsigned char *)(a))[0] == 192) && \
+	(((unsigned char *)(a))[1] == 88) && \
+	(((unsigned char *)(a))[2] == 99)))
+
+/* IPv4 Link-local addresses: 169.254.0.0/16.  */
+#define IS_IPV4_LINK_ADDRESS(a) \
+	((((unsigned char *)(a))[0] == 169) && \
+	(((unsigned char *)(a))[1] == 254))
+
+/* RFC 1918 "Address Allocation for Private Internets" defines the IPv4
+ * private address space as the following:
+ *
+ * 10.0.0.0 - 10.255.255.255 (10/8 prefix)
+ * 172.16.0.0.0 - 172.31.255.255 (172.16/12 prefix)
+ * 192.168.0.0 - 192.168.255.255 (192.168/16 prefix)
+ */
+#define IS_IPV4_PRIVATE_ADDRESS(a) \
+	((((unsigned char *)(a))[0] == 10) || \
+	((((unsigned char *)(a))[0] == 172) && \
+	(((unsigned char *)(a))[1] >= 16) && \
+	(((unsigned char *)(a))[1] < 32)) || \
+	((((unsigned char *)(a))[0] == 192) && \
+	(((unsigned char *)(a))[1] == 168)))
+
+/* Flags used for the bind address copy functions.  */
+#define SCTP_ADDR6_ALLOWED	0x00000001	/* IPv6 address is allowed by
+						   local sock family */
+#define SCTP_ADDR4_PEERSUPP	0x00000002	/* IPv4 address is supported by
+						   peer */
+#define SCTP_ADDR6_PEERSUPP	0x00000004	/* IPv6 address is supported by
+						   peer */
+
+/* Reasons to retransmit. */
+typedef enum {
+	SCTP_RTXR_T3_RTX,
+	SCTP_RTXR_FAST_RTX,
+	SCTP_RTXR_PMTUD,
+} sctp_retransmit_reason_t;
+
+/* Reasons to lower cwnd. */
+typedef enum {
+	SCTP_LOWER_CWND_T3_RTX,
+	SCTP_LOWER_CWND_FAST_RTX,
+	SCTP_LOWER_CWND_ECNE,
+	SCTP_LOWER_CWND_INACTIVE,
+} sctp_lower_cwnd_t;
+
+#endif /* __sctp_constants_h__ */
diff -urN linux-2.4.23-pre5/include/net/sctp/sctp.h linux-2.4.23-pre6/include/net/sctp/sctp.h
--- linux-2.4.23-pre5/include/net/sctp/sctp.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/include/net/sctp/sctp.h	2003-10-01 10:38:57.000000000 -0700
@@ -0,0 +1,556 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001-2003 International Business Machines, Corp.
+ * Copyright (c) 2001-2003 Intel Corp.
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * The base lksctp header.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Xingang Guo           <xingang.guo@intel.com>
+ *    Jon Grimm             <jgrimm@us.ibm.com>
+ *    Daisy Chang           <daisyc@us.ibm.com>
+ *    Sridhar Samudrala     <sri@us.ibm.com>
+ *    Ardelle Fan           <ardelle.fan@intel.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#ifndef __net_sctp_h__
+#define __net_sctp_h__
+
+/* Header Strategy.
+ *    Start getting some control over the header file depencies:
+ *       includes
+ *       constants
+ *       structs
+ *       prototypes
+ *       macros, externs, and inlines
+ *
+ *   Move test_frame specific items out of the kernel headers
+ *   and into the test frame headers.   This is not perfect in any sense
+ *   and will continue to evolve.
+ */
+
+
+#include <linux/config.h>
+
+#ifdef TEST_FRAME
+#undef CONFIG_PROC_FS
+#undef CONFIG_SCTP_DBG_OBJCNT
+#undef CONFIG_SYSCTL
+#endif /* TEST_FRAME */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/in.h>
+#include <linux/tty.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/crypto.h>
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#endif
+
+#include <net/sctp/compat.h>
+#include <asm/uaccess.h>
+#include <asm/page.h>
+#include <net/sock.h>
+#include <net/snmp.h>
+#include <net/sctp/structs.h>
+#include <net/sctp/constants.h>
+#include <net/sctp/sm.h>
+
+
+/* Set SCTP_DEBUG flag via config if not already set. */
+#ifndef SCTP_DEBUG
+#ifdef CONFIG_SCTP_DBG_MSG
+#define SCTP_DEBUG	1
+#else
+#define SCTP_DEBUG      0
+#endif /* CONFIG_SCTP_DBG */
+#endif /* SCTP_DEBUG */
+
+#ifdef CONFIG_IP_SCTP_MODULE
+#define SCTP_PROTOSW_FLAG 0
+#else /* static! */
+#define SCTP_PROTOSW_FLAG INET_PROTOSW_PERMANENT
+#endif
+
+
+/* Certain internal static functions need to be exported when
+ * compiled into the test frame.
+ */
+#ifndef SCTP_STATIC
+#define SCTP_STATIC static
+#endif
+
+/*
+ * Function declarations.
+ */
+
+/*
+ * sctp_protocol.c
+ */
+extern struct sctp_protocol sctp_proto;
+extern struct sock *sctp_get_ctl_sock(void);
+extern int sctp_copy_local_addr_list(struct sctp_protocol  *,
+				     struct sctp_bind_addr *,
+				     sctp_scope_t, int gfp, int flags);
+extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family);
+extern int sctp_register_pf(struct sctp_pf *, sa_family_t);
+
+/*
+ * sctp/socket.c
+ */
+extern int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb);
+extern int sctp_inet_listen(struct socket *sock, int backlog);
+extern void sctp_write_space(struct sock *sk);
+extern unsigned int sctp_poll(struct file *file, struct socket *sock,
+		poll_table *wait);
+
+/*
+ * sctp/primitive.c
+ */
+extern int sctp_primitive_ASSOCIATE(struct sctp_association *, void *arg);
+extern int sctp_primitive_SHUTDOWN(struct sctp_association *, void *arg);
+extern int sctp_primitive_ABORT(struct sctp_association *, void *arg);
+extern int sctp_primitive_SEND(struct sctp_association *, void *arg);
+extern int sctp_primitive_REQUESTHEARTBEAT(struct sctp_association *, void *arg);
+
+/*
+ * sctp/crc32c.c
+ */
+extern __u32 sctp_start_cksum(__u8 *ptr, __u16 count);
+extern __u32 sctp_update_cksum(__u8 *ptr, __u16 count, __u32 cksum);
+extern __u32 sctp_end_cksum(__u32 cksum);
+extern __u32 sctp_update_copy_cksum(__u8 *, __u8 *, __u16 count, __u32 cksum);
+
+/*
+ * sctp/input.c
+ */
+extern int sctp_rcv(struct sk_buff *skb);
+extern void sctp_v4_err(struct sk_buff *skb, u32 info);
+extern void sctp_hash_established(struct sctp_association *);
+extern void __sctp_hash_established(struct sctp_association *);
+extern void sctp_unhash_established(struct sctp_association *);
+extern void __sctp_unhash_established(struct sctp_association *);
+extern void sctp_hash_endpoint(struct sctp_endpoint *);
+extern void __sctp_hash_endpoint(struct sctp_endpoint *);
+extern void sctp_unhash_endpoint(struct sctp_endpoint *);
+extern void __sctp_unhash_endpoint(struct sctp_endpoint *);
+extern struct sctp_association *__sctp_lookup_association(
+	const union sctp_addr *,
+	const union sctp_addr *,
+	struct sctp_transport **);
+extern struct sock *sctp_err_lookup(int family, struct sk_buff *,
+				    struct sctphdr *, struct sctp_endpoint **,
+				    struct sctp_association **,
+				    struct sctp_transport **);
+extern void sctp_err_finish(struct sock *, struct sctp_endpoint *,
+			    struct sctp_association *);
+extern void sctp_icmp_frag_needed(struct sock *, struct sctp_association *,
+				  struct sctp_transport *t, __u32 pmtu);
+
+/*
+ *  Section:  Macros, externs, and inlines
+ */
+
+
+#ifdef TEST_FRAME
+#include <test_frame.h>
+#else
+
+/* spin lock wrappers. */
+#define sctp_spin_lock_irqsave(lock, flags) spin_lock_irqsave(lock, flags)
+#define sctp_spin_unlock_irqrestore(lock, flags)  \
+       spin_unlock_irqrestore(lock, flags)
+#define sctp_local_bh_disable() local_bh_disable()
+#define sctp_local_bh_enable()  local_bh_enable()
+#define sctp_spin_lock(lock)    spin_lock(lock)
+#define sctp_spin_unlock(lock)  spin_unlock(lock)
+#define sctp_write_lock(lock)   write_lock(lock)
+#define sctp_write_unlock(lock) write_unlock(lock)
+#define sctp_read_lock(lock)    read_lock(lock)
+#define sctp_read_unlock(lock)  read_unlock(lock)
+
+/* sock lock wrappers. */
+#define sctp_lock_sock(sk)       lock_sock(sk)
+#define sctp_release_sock(sk)    release_sock(sk)
+#define sctp_bh_lock_sock(sk)    bh_lock_sock(sk)
+#define sctp_bh_unlock_sock(sk)  bh_unlock_sock(sk)
+#define SCTP_SOCK_SLEEP_PRE(sk)  SOCK_SLEEP_PRE(sk)
+#define SCTP_SOCK_SLEEP_POST(sk) SOCK_SLEEP_POST(sk)
+
+/* SCTP SNMP MIB stats handlers */
+DECLARE_SNMP_STAT(struct sctp_mib, sctp_statistics);
+#define SCTP_INC_STATS(field)      SNMP_INC_STATS(sctp_statistics, field)
+#define SCTP_INC_STATS_BH(field)   SNMP_INC_STATS_BH(sctp_statistics, field)
+#define SCTP_INC_STATS_USER(field) SNMP_INC_STATS_USER(sctp_statistics, field)
+#define SCTP_DEC_STATS(field)      SNMP_DEC_STATS(sctp_statistics, field)
+
+/* Determine if this is a valid kernel address.  */
+static inline int sctp_is_valid_kaddr(unsigned long addr)
+{
+	struct page *page;
+
+	/* Make sure the address is not in the user address space. */
+	if (addr < PAGE_OFFSET)
+		return 0;
+
+	page = virt_to_page((void*)addr);
+
+	/* Is this page valid? */
+	if (!virt_addr_valid((void*)addr) || PageReserved(page))
+		return 0;
+
+	return 1;
+}
+
+#endif /* !TEST_FRAME */
+
+
+/* Print debugging messages.  */
+#if SCTP_DEBUG
+extern int sctp_debug_flag;
+#define SCTP_DEBUG_PRINTK(whatever...) \
+	((void) (sctp_debug_flag && printk(KERN_DEBUG whatever)))
+#define SCTP_ENABLE_DEBUG { sctp_debug_flag = 1; }
+#define SCTP_DISABLE_DEBUG { sctp_debug_flag = 0; }
+
+#define SCTP_ASSERT(expr, str, func) \
+	if (!(expr)) { \
+		SCTP_DEBUG_PRINTK("Assertion Failed: %s(%s) at %s:%s:%d\n", \
+			str, (#expr), __FILE__, __FUNCTION__, __LINE__); \
+		func; \
+	}
+
+#else	/* SCTP_DEBUG */
+
+#define SCTP_DEBUG_PRINTK(whatever...)
+#define SCTP_ENABLE_DEBUG
+#define SCTP_DISABLE_DEBUG
+#define SCTP_ASSERT(expr, str, func)
+
+#endif /* SCTP_DEBUG */
+
+
+/*
+ * Macros for keeping a global reference of object allocations.
+ */
+#ifdef CONFIG_SCTP_DBG_OBJCNT
+
+extern atomic_t sctp_dbg_objcnt_sock;
+extern atomic_t sctp_dbg_objcnt_ep;
+extern atomic_t sctp_dbg_objcnt_assoc;
+extern atomic_t sctp_dbg_objcnt_transport;
+extern atomic_t sctp_dbg_objcnt_chunk;
+extern atomic_t sctp_dbg_objcnt_bind_addr;
+extern atomic_t sctp_dbg_objcnt_addr;
+extern atomic_t sctp_dbg_objcnt_ssnmap;
+
+/* Macros to atomically increment/decrement objcnt counters.  */
+#define SCTP_DBG_OBJCNT_INC(name) \
+atomic_inc(&sctp_dbg_objcnt_## name)
+#define SCTP_DBG_OBJCNT_DEC(name) \
+atomic_dec(&sctp_dbg_objcnt_## name)
+#define SCTP_DBG_OBJCNT(name) \
+atomic_t sctp_dbg_objcnt_## name = ATOMIC_INIT(0)
+
+/* Macro to help create new entries in in the global array of
+ * objcnt counters.
+ */
+#define SCTP_DBG_OBJCNT_ENTRY(name) \
+{.label= #name, .counter= &sctp_dbg_objcnt_## name}
+
+extern void sctp_dbg_objcnt_init(void);
+extern void sctp_dbg_objcnt_exit(void);
+
+#else
+
+#define SCTP_DBG_OBJCNT_INC(name)
+#define SCTP_DBG_OBJCNT_DEC(name)
+
+static inline void sctp_dbg_objcnt_init(void) { return; }
+static inline void sctp_dbg_objcnt_exit(void) { return; }
+
+#endif /* CONFIG_SCTP_DBG_OBJCOUNT */
+
+#if defined CONFIG_SYSCTL
+extern void sctp_sysctl_register(void);
+extern void sctp_sysctl_unregister(void);
+#else
+static inline void sctp_sysctl_register(void) { return; }
+static inline void sctp_sysctl_unregister(void) { return; }
+#endif
+
+/* Size of Supported Address Parameter for 'x' address types. */
+#define SCTP_SAT_LEN(x) (sizeof(struct sctp_paramhdr) + (x) * sizeof(__u16))
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+
+extern int sctp_v6_init(void);
+extern void sctp_v6_exit(void);
+extern void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+			int type, int code, int offset, __u32 info);
+
+#else /* #ifdef defined(CONFIG_IPV6) */
+
+static inline int sctp_v6_init(void) { return 0; }
+static inline void sctp_v6_exit(void) { return; }
+
+#endif /* #if defined(CONFIG_IPV6) */
+
+/* Some wrappers, in case crypto not available. */
+#if defined (CONFIG_CRYPTO_HMAC)
+#define sctp_crypto_alloc_tfm crypto_alloc_tfm
+#define sctp_crypto_free_tfm crypto_free_tfm
+#define sctp_crypto_hmac crypto_hmac
+#else
+#define sctp_crypto_alloc_tfm(x...) NULL
+#define sctp_crypto_free_tfm(x...)
+#define sctp_crypto_hmac(x...)
+#endif
+
+
+/* Map an association to an assoc_id. */
+static inline sctp_assoc_t sctp_assoc2id(const struct sctp_association *asoc)
+{
+	return (sctp_assoc_t) asoc;
+}
+
+
+/* Look up the association by its id.  */
+struct sctp_association *sctp_id2assoc(struct sock *sk, sctp_assoc_t id);
+
+
+/* A macro to walk a list of skbs.  */
+#define sctp_skb_for_each(pos, head, tmp) \
+for (pos = (head)->next;\
+     tmp = (pos)->next, pos != ((struct sk_buff *)(head));\
+     pos = tmp)
+
+
+/* A helper to append an entire skb list (list) to another (head). */
+static inline void sctp_skb_list_tail(struct sk_buff_head *list,
+				      struct sk_buff_head *head)
+{
+	unsigned long flags;
+
+	sctp_spin_lock_irqsave(&head->lock, flags);
+	sctp_spin_lock(&list->lock);
+
+	list_splice((struct list_head *)list, (struct list_head *)head->prev);
+
+	head->qlen += list->qlen;
+	list->qlen = 0;
+
+	sctp_spin_unlock(&list->lock);
+	sctp_spin_unlock_irqrestore(&head->lock, flags);
+}
+
+/**
+ *	sctp_list_dequeue - remove from the head of the queue
+ *	@list: list to dequeue from
+ *
+ *	Remove the head of the list. The head item is
+ *	returned or %NULL if the list is empty.
+ */
+
+static inline struct list_head *sctp_list_dequeue(struct list_head *list)
+{
+	struct list_head *result = NULL;
+
+	if (list->next != list) {
+		result = list->next;
+		list->next = result->next;
+		list->next->prev = list;
+		INIT_LIST_HEAD(result);
+	}
+	return result;
+}
+
+/* Calculate the size (in bytes) occupied by the data of an iovec.  */
+static inline size_t get_user_iov_size(struct iovec *iov, int iovlen)
+{
+	size_t retval = 0;
+
+	for (; iovlen > 0; --iovlen) {
+		retval += iov->iov_len;
+		iov++;
+	}
+
+	return retval;
+}
+
+/* Generate a random jitter in the range of -50% ~ +50% of input RTO. */
+static inline __s32 sctp_jitter(__u32 rto)
+{
+	static __u32 sctp_rand;
+	__s32 ret;
+
+	sctp_rand += jiffies;
+	sctp_rand ^= (sctp_rand << 12);
+	sctp_rand ^= (sctp_rand >> 20);
+
+	/* Choose random number from 0 to rto, then move to -50% ~ +50%
+	 * of rto.
+	 */
+	ret = sctp_rand % rto - (rto >> 1);
+	return ret;
+}
+
+/* Break down data chunks at this point.  */
+static inline int sctp_frag_point(int pmtu)
+{
+	pmtu -= SCTP_IP_OVERHEAD + sizeof(struct sctp_data_chunk);
+	pmtu -= sizeof(struct sctp_sack_chunk);
+
+	return pmtu;
+}
+
+/* Walk through a list of TLV parameters.  Don't trust the
+ * individual parameter lengths and instead depend on
+ * the chunk length to indicate when to stop.  Make sure
+ * there is room for a param header too.
+ */
+#define sctp_walk_params(pos, chunk, member)\
+_sctp_walk_params((pos), (chunk), ntohs((chunk)->chunk_hdr.length), member)
+
+#define _sctp_walk_params(pos, chunk, end, member)\
+for (pos.v = chunk->member;\
+     pos.v <= (void *)chunk + end - sizeof(sctp_paramhdr_t) &&\
+     pos.v <= (void *)chunk + end - WORD_ROUND(ntohs(pos.p->length)); \
+     pos.v += WORD_ROUND(ntohs(pos.p->length)))
+
+/* Round an int up to the next multiple of 4.  */
+#define WORD_ROUND(s) (((s)+3)&~3)
+
+/* Make a new instance of type.  */
+#define t_new(type, flags)	(type *)kmalloc(sizeof(type), flags)
+
+/* Compare two timevals.  */
+#define tv_lt(s, t) \
+   (s.tv_sec < t.tv_sec || (s.tv_sec == t.tv_sec && s.tv_usec < t.tv_usec))
+
+/* Stolen from net/profile.h.  Using it from there is more grief than
+ * it is worth.
+ */
+static inline void tv_add(const struct timeval *entered, struct timeval *leaved)
+{
+	time_t usecs = leaved->tv_usec + entered->tv_usec;
+	time_t secs = leaved->tv_sec + entered->tv_sec;
+
+	if (usecs >= 1000000) {
+		usecs -= 1000000;
+		secs++;
+	}
+	leaved->tv_sec = secs;
+	leaved->tv_usec = usecs;
+}
+
+
+/* External references. */
+
+extern struct proto sctp_prot;
+extern struct proc_dir_entry *proc_net_sctp;
+extern void sctp_put_port(struct sock *sk);
+
+/* Static inline functions. */
+
+/* Return the SCTP protocol structure. */
+static inline struct sctp_protocol *sctp_get_protocol(void)
+{
+	return &sctp_proto;
+}
+
+/* Convert from an IP version number to an Address Family symbol.  */
+static inline int ipver2af(__u8 ipver)
+{
+	switch (ipver) {
+	case 4:
+	        return  AF_INET;
+	case 6:
+		return AF_INET6;
+	default:
+		return 0;
+	};
+}
+
+/* Perform some sanity checks. */
+static inline int sctp_sanity_check(void)
+{
+	SCTP_ASSERT(sizeof(struct sctp_ulpevent) <=
+		    sizeof(((struct sk_buff *)0)->cb),
+		    "SCTP: ulpevent does not fit in skb!\n", return 0);
+
+	return 1;
+}
+
+/* Warning: The following hash functions assume a power of two 'size'. */
+/* This is the hash function for the SCTP port hash table. */
+static inline int sctp_phashfn(__u16 lport)
+{
+	struct sctp_protocol *sctp_proto = sctp_get_protocol();
+	return (lport & (sctp_proto->port_hashsize - 1));
+}
+
+/* This is the hash function for the endpoint hash table. */
+static inline int sctp_ep_hashfn(__u16 lport)
+{
+	struct sctp_protocol *sctp_proto = sctp_get_protocol();
+	return (lport & (sctp_proto->ep_hashsize - 1));
+}
+
+/* This is the hash function for the association hash table. */
+static inline int sctp_assoc_hashfn(__u16 lport, __u16 rport)
+{
+	struct sctp_protocol *sctp_proto = sctp_get_protocol();
+	int h = (lport << 16) + rport;
+	h ^= h>>8;
+	return (h & (sctp_proto->assoc_hashsize - 1));
+}
+
+/* This is the hash function for the association hash table.  This is
+ * not used yet, but could be used as a better hash function when
+ * we have a vtag.
+ */
+static inline int sctp_vtag_hashfn(__u16 lport, __u16 rport, __u32 vtag)
+{
+	struct sctp_protocol *sctp_proto = sctp_get_protocol();
+	int h = (lport << 16) + rport;
+	h ^= vtag;
+	return (h & (sctp_proto->assoc_hashsize-1));
+}
+
+#endif /* __net_sctp_h__ */
diff -urN linux-2.4.23-pre5/include/net/sctp/sla1.h linux-2.4.23-pre6/include/net/sctp/sla1.h
--- linux-2.4.23-pre5/include/net/sctp/sla1.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/include/net/sctp/sla1.h	2003-10-01 10:38:57.000000000 -0700
@@ -0,0 +1,80 @@
+/* SCTP reference Implementation 
+ * Copyright (C) 1999 Cisco, Inc. 
+ * Copyright (C) 1999 Motorola, Inc.
+ *
+ * This file originates from Randy Stewart's SCTP reference Implementation.
+ * 
+ * The SCTP reference implementation is distributed in the hope that it 
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.  
+ * 
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ * 
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by: 
+ *    Randy Stewart <rstewar1@email.mot.com>
+ *    Ken Morneau   <kmorneau@cisco.com> 
+ *    Qiaobing Xie  <qxie1@email.mot.com>
+ */
+
+#ifndef __SLA1_h__
+#define __SLA1_h__
+
+struct SLA_1_Context {
+	unsigned int A;
+	unsigned int B;
+	unsigned int C;
+	unsigned int D;
+	unsigned int E;
+	unsigned int H0;
+	unsigned int H1;
+	unsigned int H2;
+	unsigned int H3;
+	unsigned int H4;
+	unsigned int words[80];
+	unsigned int TEMP;
+
+	/* block I am collecting to process */
+	char SLAblock[64];
+
+	/* collected so far */
+	int howManyInBlock;
+	unsigned int runningTotal;
+};
+
+
+#define F1(B,C,D) (((B & C) | ((~B) & D)))       /* 0  <= t <= 19 */
+#define F2(B,C,D) (B ^ C ^ D)                   /* 20 <= t <= 39 */
+#define F3(B,C,D) ((B & C) | (B & D) | (C & D)) /* 40 <= t <= 59 */
+#define F4(B,C,D) (B ^ C ^ D)                   /*600 <= t <= 79 */
+/* circular shift */
+
+#define CSHIFT(A,B) ((B << A) | (B >> (32-A)))
+
+#define K1 0x5a827999       /* 0  <= t <= 19 */
+#define K2 0x6ed9eba1       /* 20 <= t <= 39 */
+#define K3 0x8f1bbcdc       /* 40 <= t <= 59 */
+#define K4 0xca62c1d6       /* 60 <= t <= 79 */
+
+#define H0INIT 0x67452301
+#define H1INIT 0xefcdab89
+#define H2INIT 0x98badcfe
+#define H3INIT 0x10325476
+#define H4INIT 0xc3d2e1f0
+
+extern void SLA1_Init(struct SLA_1_Context *);
+extern void SLA1_Process(struct SLA_1_Context *, const unsigned char *, int);
+extern void SLA1_Final(struct SLA_1_Context *, unsigned char *);
+
+#endif
diff -urN linux-2.4.23-pre5/include/net/sctp/sm.h linux-2.4.23-pre6/include/net/sctp/sm.h
--- linux-2.4.23-pre5/include/net/sctp/sm.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/include/net/sctp/sm.h	2003-10-01 10:38:57.000000000 -0700
@@ -0,0 +1,480 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001 Intel Corp.
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * This file is part of the implementation of the add-IP extension,
+ * based on <draft-ietf-tsvwg-addip-sctp-02.txt> June 29, 2001,
+ * for the SCTP kernel reference Implementation.
+ *
+ * These are definitions needed by the state machine.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email addresses:
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Karl Knutson <karl@athena.chicago.il.us>
+ *    Xingang Guo <xingang.guo@intel.com>
+ *    Jon Grimm <jgrimm@us.ibm.com>
+ *    Dajiang Zhang <dajiang.zhang@nokia.com>
+ *    Sridhar Samudrala <sri@us.ibm.com>
+ *    Daisy Chang <daisyc@us.ibm.com>
+ *    Ardelle Fan <ardelle.fan@intel.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+
+#include <linux/types.h>
+#include <linux/compiler.h>
+#include <linux/slab.h>
+#include <linux/in.h>
+#include <net/sctp/command.h>
+#include <net/sctp/sctp.h>
+
+#ifndef __sctp_sm_h__
+#define __sctp_sm_h__
+
+/*
+ * Possible values for the disposition are:
+ */
+typedef enum {
+	SCTP_DISPOSITION_DISCARD,	 /* No further processing.  */
+	SCTP_DISPOSITION_CONSUME,	 /* Process return values normally.  */
+	SCTP_DISPOSITION_NOMEM,		 /* We ran out of memory--recover.  */
+	SCTP_DISPOSITION_DELETE_TCB,	 /* Close the association.  */
+	SCTP_DISPOSITION_ABORT,		 /* Close the association NOW.  */
+	SCTP_DISPOSITION_VIOLATION,	 /* The peer is misbehaving.  */
+	SCTP_DISPOSITION_NOT_IMPL,	 /* This entry is not implemented.  */
+	SCTP_DISPOSITION_ERROR,		 /* This is plain old user error.  */
+	SCTP_DISPOSITION_BUG,		 /* This is a bug.  */
+} sctp_disposition_t;
+
+typedef struct {
+	int name;
+	int action;
+} sctp_sm_command_t;
+
+typedef sctp_disposition_t (sctp_state_fn_t) (const struct sctp_endpoint *,
+					      const struct sctp_association *,
+					      const sctp_subtype_t type,
+					      void *arg,
+					      sctp_cmd_seq_t *);
+typedef void (sctp_timer_event_t) (unsigned long);
+typedef struct {
+	sctp_state_fn_t *fn;
+	char *name;
+} sctp_sm_table_entry_t;
+
+/* A naming convention of "sctp_sf_xxx" applies to all the state functions
+ * currently in use.
+ */
+
+/* Prototypes for generic state functions. */
+sctp_state_fn_t sctp_sf_not_impl;
+sctp_state_fn_t sctp_sf_bug;
+
+/* Prototypes for gener timer state functions. */
+sctp_state_fn_t sctp_sf_timer_ignore;
+
+/* Prototypes for chunk state functions. */
+sctp_state_fn_t sctp_sf_do_9_1_abort;
+sctp_state_fn_t sctp_sf_cookie_wait_abort;
+sctp_state_fn_t sctp_sf_cookie_echoed_abort;
+sctp_state_fn_t sctp_sf_shutdown_pending_abort;
+sctp_state_fn_t sctp_sf_shutdown_sent_abort;
+sctp_state_fn_t sctp_sf_shutdown_ack_sent_abort;
+sctp_state_fn_t sctp_sf_do_5_1B_init;
+sctp_state_fn_t sctp_sf_do_5_1C_ack;
+sctp_state_fn_t sctp_sf_do_5_1D_ce;
+sctp_state_fn_t sctp_sf_do_5_1E_ca;
+sctp_state_fn_t sctp_sf_do_4_C;
+sctp_state_fn_t sctp_sf_eat_data_6_2;
+sctp_state_fn_t sctp_sf_eat_data_fast_4_4;
+sctp_state_fn_t sctp_sf_eat_sack_6_2;
+sctp_state_fn_t sctp_sf_tabort_8_4_8;
+sctp_state_fn_t sctp_sf_operr_notify;
+sctp_state_fn_t sctp_sf_t1_timer_expire;
+sctp_state_fn_t sctp_sf_t2_timer_expire;
+sctp_state_fn_t sctp_sf_t5_timer_expire;
+sctp_state_fn_t sctp_sf_sendbeat_8_3;
+sctp_state_fn_t sctp_sf_beat_8_3;
+sctp_state_fn_t sctp_sf_backbeat_8_3;
+sctp_state_fn_t sctp_sf_do_9_2_final;
+sctp_state_fn_t sctp_sf_do_9_2_shutdown;
+sctp_state_fn_t sctp_sf_do_ecn_cwr;
+sctp_state_fn_t sctp_sf_do_ecne;
+sctp_state_fn_t sctp_sf_ootb;
+sctp_state_fn_t sctp_sf_shut_8_4_5;
+sctp_state_fn_t sctp_sf_pdiscard;
+sctp_state_fn_t sctp_sf_violation;
+sctp_state_fn_t sctp_sf_discard_chunk;
+sctp_state_fn_t sctp_sf_do_5_2_1_siminit;
+sctp_state_fn_t sctp_sf_do_5_2_2_dupinit;
+sctp_state_fn_t sctp_sf_do_5_2_4_dupcook;
+sctp_state_fn_t sctp_sf_unk_chunk;
+sctp_state_fn_t sctp_sf_do_8_5_1_E_sa;
+sctp_state_fn_t sctp_sf_cookie_echoed_err;
+sctp_state_fn_t sctp_sf_do_5_2_6_stale;
+
+/* Prototypes for primitive event state functions.  */
+sctp_state_fn_t sctp_sf_do_prm_asoc;
+sctp_state_fn_t sctp_sf_do_prm_send;
+sctp_state_fn_t sctp_sf_do_9_2_prm_shutdown;
+sctp_state_fn_t sctp_sf_cookie_wait_prm_shutdown;
+sctp_state_fn_t sctp_sf_cookie_echoed_prm_shutdown;
+sctp_state_fn_t sctp_sf_do_9_1_prm_abort;
+sctp_state_fn_t sctp_sf_cookie_wait_prm_abort;
+sctp_state_fn_t sctp_sf_cookie_echoed_prm_abort;
+sctp_state_fn_t sctp_sf_shutdown_pending_prm_abort;
+sctp_state_fn_t sctp_sf_shutdown_sent_prm_abort;
+sctp_state_fn_t sctp_sf_shutdown_ack_sent_prm_abort;
+sctp_state_fn_t sctp_sf_error_closed;
+sctp_state_fn_t sctp_sf_error_shutdown;
+sctp_state_fn_t sctp_sf_ignore_primitive;
+sctp_state_fn_t sctp_sf_do_prm_requestheartbeat;
+
+/* Prototypes for other event state functions.  */
+sctp_state_fn_t sctp_sf_do_9_2_start_shutdown;
+sctp_state_fn_t sctp_sf_do_9_2_shutdown_ack;
+sctp_state_fn_t sctp_sf_ignore_other;
+
+/* Prototypes for timeout event state functions.  */
+sctp_state_fn_t sctp_sf_do_6_3_3_rtx;
+sctp_state_fn_t sctp_sf_do_6_2_sack;
+sctp_state_fn_t sctp_sf_autoclose_timer_expire;
+
+
+/* These are state functions which are either obsolete or not in use yet.
+ * If any of these functions needs to be revived, it should be renamed with
+ * the "sctp_sf_xxx" prefix, and be moved to the above prototype groups.
+ */
+
+/* Prototypes for chunk state functions.  Not in use. */
+sctp_state_fn_t sctp_sf_do_9_2_reshutack;
+sctp_state_fn_t sctp_sf_do_9_2_reshut;
+sctp_state_fn_t sctp_sf_do_9_2_shutack;
+
+sctp_state_fn_t lucky;
+sctp_state_fn_t other_stupid;
+
+/* Prototypes for timeout event state functions.  Not in use. */
+sctp_state_fn_t sctp_do_4_2_reinit;
+sctp_state_fn_t sctp_do_4_3_reecho;
+sctp_state_fn_t sctp_do_9_2_reshut;
+sctp_state_fn_t sctp_do_9_2_reshutack;
+sctp_state_fn_t sctp_do_8_3_hb_err;
+sctp_state_fn_t sctp_heartoff;
+
+/* Prototypes for addip related state functions.  Not in use. */
+sctp_state_fn_t sctp_addip_do_asconf;
+sctp_state_fn_t sctp_addip_do_asconf_ack;
+
+/* Prototypes for utility support functions.  */
+__u8 sctp_get_chunk_type(struct sctp_chunk *chunk);
+sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type,
+					    sctp_state_t state,
+					    sctp_subtype_t event_subtype);
+int sctp_chunk_iif(const struct sctp_chunk *);
+struct sctp_association *sctp_make_temp_asoc(const struct sctp_endpoint *,
+					     struct sctp_chunk *,
+					     int gfp);
+__u32 sctp_generate_verification_tag(void);
+void sctp_populate_tie_tags(__u8 *cookie, __u32 curTag, __u32 hisTag);
+
+/* Prototypes for chunk-building functions.  */
+sctp_chunk_t *sctp_make_init(const struct sctp_association *,
+			     const sctp_bind_addr_t *,
+			     int gfp, int vparam_len);
+sctp_chunk_t *sctp_make_init_ack(const struct sctp_association *,
+				 const sctp_chunk_t *,
+				 const int gfp,
+				 const int unkparam_len);
+sctp_chunk_t *sctp_make_cookie_echo(const struct sctp_association *,
+				    const sctp_chunk_t *);
+sctp_chunk_t *sctp_make_cookie_ack(const struct sctp_association *,
+				   const sctp_chunk_t *);
+sctp_chunk_t *sctp_make_cwr(const struct sctp_association *,
+				 const __u32 lowest_tsn,
+				 const sctp_chunk_t *);
+sctp_chunk_t *sctp_make_datafrag(struct sctp_association *,
+				 const struct sctp_sndrcvinfo *sinfo,
+				 int len, const __u8 *data,
+				 __u8 flags, __u16 ssn);
+sctp_chunk_t * sctp_make_datafrag_empty(struct sctp_association *,
+					const struct sctp_sndrcvinfo *sinfo,
+					int len, const __u8 flags,
+					__u16 ssn);
+sctp_chunk_t *sctp_make_data(struct sctp_association *,
+			     const struct sctp_sndrcvinfo *sinfo,
+			     int len, const __u8 *data);
+sctp_chunk_t *sctp_make_data_empty(struct sctp_association *,
+				   const struct sctp_sndrcvinfo *, int len);
+sctp_chunk_t *sctp_make_ecne(const struct sctp_association *,
+				  const __u32);
+sctp_chunk_t *sctp_make_sack(const struct sctp_association *);
+sctp_chunk_t *sctp_make_shutdown(const struct sctp_association *asoc);
+sctp_chunk_t *sctp_make_shutdown_ack(const struct sctp_association *asoc,
+					  const sctp_chunk_t *);
+sctp_chunk_t *sctp_make_shutdown_complete(const struct sctp_association *,
+					  const sctp_chunk_t *);
+void sctp_init_cause(sctp_chunk_t *, __u16 cause, const void *, size_t);
+sctp_chunk_t *sctp_make_abort(const struct sctp_association *,
+			      const sctp_chunk_t *,
+			      const size_t hint);
+sctp_chunk_t *sctp_make_abort_no_data(const struct sctp_association *,
+				      const sctp_chunk_t *,
+				      __u32 tsn);
+sctp_chunk_t *sctp_make_abort_user(const struct sctp_association *,
+				   const sctp_chunk_t *,
+				   const struct msghdr *);
+sctp_chunk_t *sctp_make_heartbeat(const struct sctp_association *,
+				  const struct sctp_transport *,
+				  const void *payload,
+				  const size_t paylen);
+sctp_chunk_t *sctp_make_heartbeat_ack(const struct sctp_association *,
+				      const sctp_chunk_t *,
+				      const void *payload,
+				      const size_t paylen);
+sctp_chunk_t *sctp_make_op_error(const struct sctp_association *,
+				 const sctp_chunk_t *chunk,
+				 __u16 cause_code,
+				 const void *payload,
+				 size_t paylen);
+void sctp_chunk_assign_tsn(sctp_chunk_t *);
+void sctp_chunk_assign_ssn(sctp_chunk_t *);
+int sctp_datachunks_from_user(struct sctp_association *,
+			      const struct sctp_sndrcvinfo *,
+			      struct msghdr *, int len,
+			      struct sk_buff_head *);
+
+
+/* Prototypes for statetable processing. */
+
+int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype,
+	       sctp_state_t state,
+               struct sctp_endpoint *,
+               struct sctp_association *asoc,
+               void *event_arg,
+               int gfp);
+
+int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
+		      sctp_state_t state,
+                      struct sctp_endpoint *,
+                      struct sctp_association *asoc,
+                      void *event_arg,
+                      sctp_disposition_t status,
+		      sctp_cmd_seq_t *commands,
+                      int gfp);
+
+/* 2nd level prototypes */
+int
+sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
+		     sctp_state_t state,
+		     struct sctp_endpoint *ep,
+		     struct sctp_association *asoc,
+		     void *event_arg,
+		     sctp_disposition_t status,
+		     sctp_cmd_seq_t *retval,
+		     int gfp);
+
+
+int sctp_gen_sack(struct sctp_association *, int force, sctp_cmd_seq_t *);
+void sctp_do_TSNdup(struct sctp_association *, sctp_chunk_t *, long gap);
+
+void sctp_generate_t3_rtx_event(unsigned long peer);
+void sctp_generate_heartbeat_event(unsigned long peer);
+
+sctp_sackhdr_t *sctp_sm_pull_sack(sctp_chunk_t *);
+struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *,
+				       const struct sctp_association *,
+				       struct sctp_chunk *chunk,
+				       const void *payload,
+				       size_t paylen);
+struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *,
+				      const struct sctp_chunk *);
+void sctp_ootb_pkt_free(struct sctp_packet *);
+
+sctp_cookie_param_t *
+sctp_pack_cookie(const struct sctp_endpoint *, const struct sctp_association *,
+		 const struct sctp_chunk *, int *cookie_len,
+		 const __u8 *, int addrs_len);
+struct sctp_association *sctp_unpack_cookie(const struct sctp_endpoint *,
+				       const struct sctp_association *,
+				       sctp_chunk_t *, int gfp, int *err,
+				       sctp_chunk_t **err_chk_p);
+int sctp_addip_addr_config(struct sctp_association *, sctp_param_t,
+			   struct sockaddr_storage*, int);
+void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep,
+				const struct sctp_association *asoc,
+				const sctp_chunk_t *chunk,
+				sctp_cmd_seq_t *commands,
+				sctp_chunk_t *err_chunk);
+
+/* 3rd level prototypes */
+__u32 sctp_generate_tag(const struct sctp_endpoint *);
+__u32 sctp_generate_tsn(const struct sctp_endpoint *);
+
+/* 4th level prototypes */
+void sctp_param2sockaddr(union sctp_addr *addr, sctp_addr_param_t *,
+			 __u16 port, int iif);
+int sctp_addr2sockaddr(const union sctp_params, union sctp_addr *);
+int sockaddr2sctp_addr(const union sctp_addr *, sctp_addr_param_t *);
+
+/* Extern declarations for major data structures.  */
+sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t, sctp_state_t);
+extern sctp_sm_table_entry_t
+primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP_STATE_NUM_STATES];
+extern sctp_sm_table_entry_t
+other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES];
+extern sctp_sm_table_entry_t
+timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES];
+extern sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES];
+
+/* These are some handy utility macros... */
+
+
+/* Get the size of a DATA chunk payload. */
+static inline __u16 sctp_data_size(sctp_chunk_t *chunk)
+{
+	__u16 size;
+
+	size = ntohs(chunk->chunk_hdr->length);
+	size -= sizeof(sctp_data_chunk_t);
+
+	return size;
+}
+
+/* Compare two TSNs */
+
+/* RFC 1982 - Serial Number Arithmetic
+ *
+ * 2. Comparison
+ *  Then, s1 is said to be equal to s2 if and only if i1 is equal to i2,
+ *  in all other cases, s1 is not equal to s2.
+ *
+ * s1 is said to be less than s2 if, and only if, s1 is not equal to s2,
+ * and
+ *
+ *      (i1 < i2 and i2 - i1 < 2^(SERIAL_BITS - 1)) or
+ *      (i1 > i2 and i1 - i2 > 2^(SERIAL_BITS - 1))
+ *
+ * s1 is said to be greater than s2 if, and only if, s1 is not equal to
+ * s2, and
+ *
+ *      (i1 < i2 and i2 - i1 > 2^(SERIAL_BITS - 1)) or
+ *      (i1 > i2 and i1 - i2 < 2^(SERIAL_BITS - 1))
+ */
+
+/*
+ * RFC 2960
+ *  1.6 Serial Number Arithmetic
+ *
+ * Comparisons and arithmetic on TSNs in this document SHOULD use Serial
+ * Number Arithmetic as defined in [RFC1982] where SERIAL_BITS = 32.
+ */
+
+enum {
+	TSN_SIGN_BIT = (1<<31)
+};
+
+static inline int TSN_lt(__u32 s, __u32 t)
+{
+	return (((s) - (t)) & TSN_SIGN_BIT);
+}
+
+static inline int TSN_lte(__u32 s, __u32 t)
+{
+	return (((s) == (t)) || (((s) - (t)) & TSN_SIGN_BIT));
+}
+
+/* Compare two SSNs */
+
+/*
+ * RFC 2960
+ *  1.6 Serial Number Arithmetic
+ *
+ * Comparisons and arithmetic on Stream Sequence Numbers in this document
+ * SHOULD use Serial Number Arithmetic as defined in [RFC1982] where
+ * SERIAL_BITS = 16.
+ */
+enum {
+	SSN_SIGN_BIT = (1<<15)
+};
+
+static inline int SSN_lt(__u16 s, __u16 t)
+{
+	return (((s) - (t)) & SSN_SIGN_BIT);
+}
+
+static inline int SSN_lte(__u16 s, __u16 t)
+{
+	return (((s) == (t)) || (((s) - (t)) & SSN_SIGN_BIT));
+}
+
+/* Run sctp_add_cmd() generating a BUG() if there is a failure.  */
+static inline void sctp_add_cmd_sf(sctp_cmd_seq_t *seq, sctp_verb_t verb, sctp_arg_t obj)
+{
+	if (unlikely(!sctp_add_cmd(seq, verb, obj)))
+		BUG();
+}
+
+/* Check VTAG of the packet matches the sender's own tag OR its peer's
+ * tag and the T bit is set in the Chunk Flags.
+ */
+static inline int
+sctp_vtag_verify_either(const sctp_chunk_t *chunk,
+			const struct sctp_association *asoc)
+{
+        /* RFC 2960 Section 8.5.1, sctpimpguide-06 Section 2.13.2
+	 *
+	 * B) The receiver of a ABORT shall accept the packet if the
+	 * Verification Tag field of the packet matches its own tag OR it
+	 * is set to its peer's tag and the T bit is set in the Chunk
+	 * Flags. Otherwise, the receiver MUST silently discard the packet
+	 * and take no further action.
+	 *
+	 * (C) The receiver of a SHUTDOWN COMPLETE shall accept the
+	 * packet if the Verification Tag field of the packet
+	 * matches its own tag OR it is set to its peer's tag and
+	 * the T bit is set in the Chunk Flags.  Otherwise, the
+	 * receiver MUST silently discard the packet and take no
+	 * further action....
+	 *
+	 */
+        if ((ntohl(chunk->sctp_hdr->vtag) == asoc->c.my_vtag) ||
+	    (sctp_test_T_bit(chunk) && (ntohl(chunk->sctp_hdr->vtag)
+	    == asoc->c.peer_vtag))) {
+                return 1;
+	}
+
+	return 0;
+}
+
+#endif /* __sctp_sm_h__ */
diff -urN linux-2.4.23-pre5/include/net/sctp/structs.h linux-2.4.23-pre6/include/net/sctp/structs.h
--- linux-2.4.23-pre5/include/net/sctp/structs.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/include/net/sctp/structs.h	2003-10-01 10:38:57.000000000 -0700
@@ -0,0 +1,1631 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001 Intel Corp.
+ * Copyright (c) 2001-2003 International Business Machines Corp.
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email addresses:
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    Randall Stewart       <randall@sctp.chicago.il.us>
+ *    Ken Morneau           <kmorneau@cisco.com>
+ *    Qiaobing Xie          <qxie1@email.mot.com>
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Karl Knutson          <karl@athena.chicago.il.us>
+ *    Jon Grimm             <jgrimm@us.ibm.com>
+ *    Xingang Guo           <xingang.guo@intel.com>
+ *    Hui Huang             <hui.huang@nokia.com>
+ *    Sridhar Samudrala     <sri@us.ibm.com>
+ *    Daisy Chang	    <daisyc@us.ibm.com>
+ *    Dajiang Zhang         <dajiang.zhang@nokia.com>
+ *    Ardelle Fan           <ardelle.fan@intel.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#ifndef __sctp_structs_h__
+#define __sctp_structs_h__
+
+#include <linux/time.h>		/* We get struct timespec.    */
+#include <linux/socket.h>	/* linux/in.h needs this!!    */
+#include <linux/in.h>		/* We get struct sockaddr_in. */
+#include <linux/in6.h>          /* We get struct in6_addr     */
+#include <asm/param.h>		/* We get MAXHOSTNAMELEN.     */
+#include <asm/atomic.h>		/* This gets us atomic counters.  */
+#include <linux/skbuff.h>	/* We need sk_buff_head. */
+#include <linux/sctp.h>         /* We need sctp* header structs.  */
+#include <linux/crypto.h>
+
+/* A convenience structure for handling sockaddr structures.
+ * We should wean ourselves off this.
+ */
+union sctp_addr {
+	struct sockaddr_in v4;
+	struct sockaddr_in6 v6;
+	struct sockaddr sa;
+};
+
+
+/* Forward declarations for data structures. */
+struct sctp_protocol;
+struct sctp_endpoint;
+struct sctp_association;
+struct sctp_transport;
+struct sctp_packet;
+struct sctp_chunk;
+struct sctp_inq;
+struct sctp_outq;
+struct sctp_bind_addr;
+struct sctp_ulpq;
+struct sctp_opt;
+struct sctp_ep_common;
+struct sctp_ssnmap;
+
+typedef struct sctp_chunk sctp_chunk_t;
+typedef struct sctp_bind_addr sctp_bind_addr_t;
+
+#include <net/sctp/tsnmap.h>
+#include <net/sctp/ulpevent.h>
+#include <net/sctp/ulpqueue.h>
+
+/* Structures useful for managing bind/connect. */
+
+typedef struct sctp_bind_bucket {
+	unsigned short 	port;
+	unsigned short	fastreuse;
+	struct sctp_bind_bucket *next;
+	struct sctp_bind_bucket **pprev;
+	struct sock	        *sk;
+} sctp_bind_bucket_t;
+
+typedef struct sctp_bind_hashbucket {
+	spinlock_t	lock;
+	struct sctp_bind_bucket	*chain;
+} sctp_bind_hashbucket_t;
+
+/* Used for hashing all associations.  */
+typedef struct sctp_hashbucket {
+	rwlock_t	lock;
+	struct sctp_ep_common  *chain;
+} sctp_hashbucket_t __attribute__((__aligned__(8)));
+
+
+/* The SCTP protocol structure. */
+struct sctp_protocol {
+	/* RFC2960 Section 14. Suggested SCTP Protocol Parameter Values
+	 *
+	 * The following protocol parameters are RECOMMENDED:
+	 *
+	 * RTO.Initial              - 3  seconds
+	 * RTO.Min                  - 1  second
+	 * RTO.Max                 -  60 seconds
+	 * RTO.Alpha                - 1/8  (3 when converted to right shifts.)
+	 * RTO.Beta                 - 1/4  (2 when converted to right shifts.)
+	 */
+	__u32 rto_initial;
+	__u32 rto_min;
+	__u32 rto_max;
+
+	/* Note: rto_alpha and rto_beta are really defined as inverse
+	 * powers of two to facilitate integer operations.
+	 */
+	int rto_alpha;
+	int rto_beta;
+
+	/* Max.Burst		    - 4 */
+	int max_burst;
+
+	/* Valid.Cookie.Life        - 60  seconds  */
+	int valid_cookie_life;
+
+	/* Whether Cookie Preservative is enabled(1) or not(0) */
+	int cookie_preserve_enable;
+
+	/* Association.Max.Retrans  - 10 attempts
+	 * Path.Max.Retrans         - 5  attempts (per destination address)
+	 * Max.Init.Retransmits     - 8  attempts
+	 */
+	int max_retrans_association;
+	int max_retrans_path;
+	int max_retrans_init;
+
+	/* HB.interval              - 30 seconds  */
+	int hb_interval;
+
+	/* The following variables are implementation specific.  */
+
+	/* Default initialization values to be applied to new associations. */
+	__u16 max_instreams;
+	__u16 max_outstreams;
+
+	/* This is a list of groups of functions for each address
+	 * family that we support.
+	 */
+	struct list_head address_families;
+
+	/* This is the hash of all endpoints. */
+	int ep_hashsize;
+	sctp_hashbucket_t *ep_hashbucket;
+
+	/* This is the hash of all associations. */
+	int assoc_hashsize;
+	sctp_hashbucket_t *assoc_hashbucket;
+
+	/* This is the sctp port control hash.  */
+	int port_hashsize;
+	int port_rover;
+	spinlock_t port_alloc_lock;  /* Protects port_rover. */
+	sctp_bind_hashbucket_t *port_hashtable;
+
+	/* This is the global local address list.
+	 * We actively maintain this complete list of interfaces on
+	 * the system by catching routing events.
+	 *
+	 * It is a list of struct sockaddr_storage_list.
+	 */
+	struct list_head local_addr_list;
+	spinlock_t local_addr_lock;
+};
+
+
+/*
+ * Pointers to address related SCTP functions.
+ * (i.e. things that depend on the address family.)
+ */
+struct sctp_af {
+	int		(*sctp_xmit)	(struct sk_buff *skb,
+					 struct sctp_transport *,
+					 int ipfragok);
+	int 		(*setsockopt)	(struct sock *sk,
+					 int level,
+					 int optname,
+					 char *optval,
+					 int optlen);
+	int 		(*getsockopt)	(struct sock *sk,
+					 int level,
+					 int optname,
+					 char *optval,
+					 int *optlen);
+	struct dst_entry *(*get_dst)	(struct sctp_association *asoc,
+					 union sctp_addr *daddr,
+					 union sctp_addr *saddr);
+	void 		(*get_saddr)	(struct sctp_association *asoc,
+					 struct dst_entry *dst,
+					 union sctp_addr *daddr,
+				 	 union sctp_addr *saddr);
+	void            (*copy_addrlist) (struct list_head *,
+					  struct net_device *);
+	void            (*dst_saddr)    (union sctp_addr *saddr,
+					 struct dst_entry *dst,
+					 unsigned short port);
+	int             (*cmp_addr)     (const union sctp_addr *addr1,
+					 const union sctp_addr *addr2);
+	void            (*addr_copy)    (union sctp_addr *dst,
+					 union sctp_addr *src);
+	void            (*from_skb)     (union sctp_addr *,
+					 struct sk_buff *skb,
+					 int saddr);
+	void            (*from_sk)      (union sctp_addr *,
+					 struct sock *sk);
+	void            (*to_sk_saddr)  (union sctp_addr *,
+					 struct sock *sk);
+	void            (*to_sk_daddr)  (union sctp_addr *,
+					 struct sock *sk);
+	int             (*addr_valid)   (union sctp_addr *);
+	sctp_scope_t    (*scope) (union sctp_addr *);
+	void            (*inaddr_any)   (union sctp_addr *, unsigned short);
+	int             (*is_any)       (const union sctp_addr *);
+	int             (*available)    (const union sctp_addr *);
+	int             (*skb_iif)      (const struct sk_buff *sk);
+	__u16		net_header_len;
+	int		sockaddr_len;
+	sa_family_t	sa_family;
+	struct list_head list;
+};
+
+struct sctp_af *sctp_get_af_specific(sa_family_t);
+int sctp_register_af(struct sctp_af *);
+
+/* Protocol family functions. */
+struct sctp_pf {
+	void (*event_msgname)(struct sctp_ulpevent *, char *, int *);
+	void (*skb_msgname)  (struct sk_buff *, char *, int *);
+	int  (*af_supported) (sa_family_t);
+	int  (*cmp_addr) (const union sctp_addr *,
+			  const union sctp_addr *,
+			  struct sctp_opt *);
+	int  (*bind_verify) (struct sctp_opt *, union sctp_addr *);
+	int  (*send_verify) (struct sctp_opt *, union sctp_addr *);
+	int  (*supported_addrs)(const struct sctp_opt *, __u16 *);
+	struct sock *(*create_accept_sk) (struct sock *sk,
+					  struct sctp_association *asoc);
+	struct sctp_af *af;
+};
+
+/* SCTP Socket type: UDP or TCP style. */
+typedef enum {
+	SCTP_SOCKET_UDP = 0,
+	SCTP_SOCKET_UDP_HIGH_BANDWIDTH,
+	SCTP_SOCKET_TCP
+} sctp_socket_type_t;
+
+/* Per socket SCTP information. */
+struct sctp_opt {
+	/* What kind of a socket is this? */
+	sctp_socket_type_t type;
+
+	/* PF_ family specific functions.  */
+	struct sctp_pf *pf;
+
+	/* Access to HMAC transform. */
+	struct crypto_tfm *hmac;
+
+	/* What is our base endpointer? */
+	struct sctp_endpoint *ep;
+
+	/* Various Socket Options.  */
+	__u16 default_stream;
+	__u32 default_ppid;
+	struct sctp_initmsg initmsg;
+	struct sctp_rtoinfo rtoinfo;
+	struct sctp_paddrparams paddrparam;
+	struct sctp_event_subscribe subscribe;
+	__u32 autoclose;
+	__u8 nodelay;
+	__u8 disable_fragments;
+	__u8 pd_mode;
+
+	/* Receive to here while partial delivery is in effect. */
+	struct sk_buff_head pd_lobby;
+};
+
+
+
+/* This is our APPLICATION-SPECIFIC state cookie.
+ * THIS IS NOT DICTATED BY THE SPECIFICATION.
+ */
+/* These are the parts of an association which we send in the cookie.
+ * Most of these are straight out of:
+ * RFC2960 12.2 Parameters necessary per association (i.e. the TCB)
+ *
+ */
+
+typedef struct sctp_cookie {
+        /* My          : Tag expected in every inbound packet and sent
+         * Verification: in the INIT or INIT ACK chunk.
+         * Tag         :
+         */
+        __u32 my_vtag;
+
+        /* Peer's      : Tag expected in every outbound packet except
+         * Verification: in the INIT chunk.
+         * Tag         :
+         */
+        __u32 peer_vtag;
+
+        /* The rest of these are not from the spec, but really need to
+         * be in the cookie.
+         */
+
+	/* My Tie Tag  : Assist in discovering a restarting association. */
+	__u32 my_ttag;
+
+	/* Peer's Tie Tag: Assist in discovering a restarting association. */
+	__u32 peer_ttag;
+
+	/* Number of inbound/outbound streams which are set
+	 * and negotiated during the INIT process.
+	 */
+	__u16 sinit_num_ostreams;
+	__u16 sinit_max_instreams;
+
+        /* When does this cookie expire? */
+        struct timeval expiration __attribute__((packed));
+
+        /* This is the first sequence number I used.  */
+	__u32 initial_tsn;
+
+	/* This holds the originating address of the INIT packet.  */
+	union sctp_addr peer_addr;
+
+	/* This is a shim for my peer's INIT packet, followed by
+	 * a copy of the raw address list of the association.
+	 * The length of the raw address list is saved in the
+	 * raw_addr_list_len field, which will be used at the time when
+	 * the association TCB is re-constructed from the cookie.
+	 */
+	__u32 raw_addr_list_len;
+	sctp_init_chunk_t peer_init[0];
+} sctp_cookie_t __attribute__((aligned(4)));
+
+
+/* The format of our cookie that we send to our peer. */
+typedef struct sctp_signed_cookie {
+	__u8 signature[SCTP_SECRET_SIZE];
+	sctp_cookie_t c;
+} sctp_signed_cookie_t;
+
+/* This is another convenience type to allocate memory for address
+ * params for the maximum size and pass such structures around
+ * internally.
+ */
+typedef union {
+	sctp_ipv4addr_param_t v4;
+	sctp_ipv6addr_param_t v6;
+} sctp_addr_param_t;
+
+/* A convenience type to allow walking through the various
+ * parameters and avoid casting all over the place.
+ */
+union sctp_params {
+	void *v;
+	sctp_paramhdr_t *p;
+	sctp_cookie_preserve_param_t *life;
+	sctp_hostname_param_t *dns;
+	sctp_cookie_param_t *cookie;
+	sctp_supported_addrs_param_t *sat;
+	sctp_ipv4addr_param_t *v4;
+	sctp_ipv6addr_param_t *v6;
+	sctp_addr_param_t *addr;
+};
+
+/* RFC 2960.  Section 3.3.5 Heartbeat.
+ *    Heartbeat Information: variable length
+ *    The Sender-specific Heartbeat Info field should normally include
+ *    information about the sender's current time when this HEARTBEAT
+ *    chunk is sent and the destination transport address to which this
+ *    HEARTBEAT is sent (see Section 8.3).
+ */
+typedef struct sctp_sender_hb_info {
+	sctp_paramhdr_t param_hdr;
+	union sctp_addr daddr;
+	unsigned long sent_at;
+} sctp_sender_hb_info_t __attribute__((packed, aligned(4)));
+
+/*
+ *  RFC 2960 1.3.2 Sequenced Delivery within Streams
+ *
+ *  The term "stream" is used in SCTP to refer to a sequence of user
+ *  messages that are to be delivered to the upper-layer protocol in
+ *  order with respect to other messages within the same stream.  This is
+ *  in contrast to its usage in TCP, where it refers to a sequence of
+ *  bytes (in this document a byte is assumed to be eight bits).
+ *  ...
+ *
+ *  This is the structure we use to track both our outbound and inbound
+ *  SSN, or Stream Sequence Numbers.
+ */
+
+struct sctp_stream {
+	__u16 *ssn;
+	unsigned int len;
+};
+
+struct sctp_ssnmap {
+	struct sctp_stream in;
+	struct sctp_stream out;
+	int malloced;
+};
+
+struct sctp_ssnmap *sctp_ssnmap_init(struct sctp_ssnmap *, __u16, __u16);
+struct sctp_ssnmap *sctp_ssnmap_new(__u16 in, __u16 out, int gfp);
+void sctp_ssnmap_free(struct sctp_ssnmap *map);
+void sctp_ssnmap_clear(struct sctp_ssnmap *map);
+
+/* What is the current SSN number for this stream? */
+static inline __u16 sctp_ssn_peek(struct sctp_stream *stream, __u16 id)
+{
+	return stream->ssn[id];
+}
+
+/* Return the next SSN number for this stream.  */
+static inline __u16 sctp_ssn_next(struct sctp_stream *stream, __u16 id)
+{
+	return stream->ssn[id]++;
+}
+
+
+/* RFC2960 1.4 Key Terms
+ *
+ * o Chunk: A unit of information within an SCTP packet, consisting of
+ * a chunk header and chunk-specific content.
+ *
+ * As a matter of convenience, we remember the SCTP common header for
+ * each chunk as well as a few other header pointers...
+ */
+struct sctp_chunk {
+	/* These first three elements MUST PRECISELY match the first
+	 * three elements of struct sk_buff.  This allows us to reuse
+	 * all the skb_* queue management functions.
+	 */
+	sctp_chunk_t *next;
+	sctp_chunk_t *prev;
+	struct sk_buff_head *list;
+
+	/* This is our link to the per-transport transmitted list.  */
+	struct list_head transmitted_list;
+
+	/* This field is used by chunks that hold fragmented data.
+	 * For the first fragment this is the list that holds the rest of
+	 * fragments. For the remaining fragments, this is the link to the
+	 * frag_list maintained in the first fragment.
+	 */
+	struct list_head frag_list;
+
+	/* This points to the sk_buff containing the actual data.  */
+	struct sk_buff *skb;
+
+	/* These are the SCTP headers by reverse order in a packet.
+	 * Note that some of these may happen more than once.  In that
+	 * case, we point at the "current" one, whatever that means
+	 * for that level of header.
+	 */
+
+	/* We point this at the FIRST TLV parameter to chunk_hdr.  */
+	union sctp_params param_hdr;
+	union {
+		__u8 *v;
+		sctp_datahdr_t *data_hdr;
+		sctp_inithdr_t *init_hdr;
+		sctp_sackhdr_t *sack_hdr;
+		sctp_heartbeathdr_t *hb_hdr;
+		sctp_sender_hb_info_t *hbs_hdr;
+		sctp_shutdownhdr_t *shutdown_hdr;
+		sctp_signed_cookie_t *cookie_hdr;
+		sctp_ecnehdr_t *ecne_hdr;
+		sctp_cwrhdr_t *ecn_cwr_hdr;
+		sctp_errhdr_t *err_hdr;
+	} subh;
+
+	__u8 *chunk_end;
+
+	sctp_chunkhdr_t *chunk_hdr;
+
+	sctp_sctphdr_t  *sctp_hdr;
+
+	/* This needs to be recoverable for SCTP_SEND_FAILED events. */
+	struct sctp_sndrcvinfo sinfo;
+
+	/* Which association does this belong to?  */
+	struct sctp_association *asoc;
+
+	/* What endpoint received this chunk? */
+	struct sctp_ep_common *rcvr;
+
+	/* We fill this in if we are calculating RTT. */
+	unsigned long sent_at;
+
+	__u8 rtt_in_progress;  /* Is this chunk used for RTT calculation? */
+	__u8 num_times_sent;   /* How man times did we send this? */
+	__u8 has_tsn;          /* Does this chunk have a TSN yet? */
+	__u8 has_ssn;          /* Does this chunk have a SSN yet? */
+	__u8 singleton;        /* Was this the only chunk in the packet? */
+	__u8 end_of_packet;    /* Was this the last chunk in the packet? */
+	__u8 ecn_ce_done;      /* Have we processed the ECN CE bit? */
+	__u8 pdiscard;	  /* Discard the whole packet now? */
+	__u8 tsn_gap_acked;	  /* Is this chunk acked by a GAP ACK? */
+	__u8 fast_retransmit;    /* Is this chunk fast retransmitted? */
+	__u8 tsn_missing_report; /* Data chunk missing counter. */
+
+	/* What is the origin IP address for this chunk?  */
+	union sctp_addr source;
+	/* Destination address for this chunk. */
+	union sctp_addr dest;
+
+	/* For an inbound chunk, this tells us where it came from.
+	 * For an outbound chunk, it tells us where we'd like it to
+	 * go.  It is NULL if we have no preference.
+	 */
+	struct sctp_transport *transport;
+};
+
+sctp_chunk_t *sctp_make_chunk(const struct sctp_association *, __u8 type,
+			      __u8 flags, int size);
+void sctp_free_chunk(sctp_chunk_t *);
+void  *sctp_addto_chunk(sctp_chunk_t *chunk, int len, const void *data);
+sctp_chunk_t *sctp_chunkify(struct sk_buff *, const struct sctp_association *,
+			    struct sock *);
+void sctp_init_addrs(sctp_chunk_t *, union sctp_addr *, union sctp_addr *);
+const union sctp_addr *sctp_source(const sctp_chunk_t *chunk);
+
+/* This is a structure for holding either an IPv6 or an IPv4 address.  */
+/* sin_family -- AF_INET or AF_INET6
+ * sin_port -- ordinary port number
+ * sin_addr -- cast to either (struct in_addr) or (struct in6_addr)
+ */
+struct sockaddr_storage_list {
+	struct list_head list;
+	union sctp_addr a;
+};
+
+typedef sctp_chunk_t *(sctp_packet_phandler_t)(struct sctp_association *);
+
+/* This structure holds lists of chunks as we are assembling for
+ * transmission.
+ */
+struct sctp_packet {
+	/* These are the SCTP header values (host order) for the packet. */
+	__u16 source_port;
+	__u16 destination_port;
+	__u32 vtag;
+
+	/* This contains the payload chunks.  */
+	struct sk_buff_head chunks;
+	/* This is the total size of all chunks INCLUDING padding.  */
+	size_t size;
+
+	/* The packet is destined for this transport address.
+	 * The function we finally use to pass down to the next lower
+	 * layer lives in the transport structure.
+	 */
+	struct sctp_transport *transport;
+
+	/* Allow a callback for getting a high priority chunk
+	 * bundled early into the packet (This is used for ECNE).
+	 */
+	sctp_packet_phandler_t *get_prepend_chunk;
+
+	/* This packet should advertise ECN capability to the network
+	 * via the ECT bit.
+	 */
+	char ecn_capable;
+
+	/* This packet contains a COOKIE-ECHO chunk. */
+	char has_cookie_echo;
+
+	/* This packet containsa SACK chunk. */
+	char has_sack;
+
+	/* SCTP cannot fragment this packet. So let ip fragment it. */
+	char ipfragok;
+
+	int malloced;
+};
+
+typedef int (sctp_outq_thandler_t)(struct sctp_outq *, void *);
+typedef int (sctp_outq_ehandler_t)(struct sctp_outq *);
+typedef struct sctp_packet *(sctp_outq_ohandler_init_t)
+	(struct sctp_packet *,
+         struct sctp_transport *,
+         __u16 sport,
+         __u16 dport);
+typedef struct sctp_packet *(sctp_outq_ohandler_config_t)
+        (struct sctp_packet *,
+	 __u32 vtag,
+	 int ecn_capable,
+	 sctp_packet_phandler_t *get_prepend_chunk);
+typedef sctp_xmit_t (sctp_outq_ohandler_t)(struct sctp_packet *,
+                                               sctp_chunk_t *);
+typedef int (sctp_outq_ohandler_force_t)(struct sctp_packet *);
+
+sctp_outq_ohandler_init_t    sctp_packet_init;
+sctp_outq_ohandler_config_t  sctp_packet_config;
+sctp_outq_ohandler_t         sctp_packet_append_chunk;
+sctp_outq_ohandler_t         sctp_packet_transmit_chunk;
+sctp_outq_ohandler_force_t   sctp_packet_transmit;
+void sctp_packet_free(struct sctp_packet *);
+
+
+/* This represents a remote transport address.
+ * For local transport addresses, we just use union sctp_addr.
+ *
+ * RFC2960 Section 1.4 Key Terms
+ *
+ *   o  Transport address:  A Transport Address is traditionally defined
+ *      by Network Layer address, Transport Layer protocol and Transport
+ *      Layer port number.  In the case of SCTP running over IP, a
+ *      transport address is defined by the combination of an IP address
+ *      and an SCTP port number (where SCTP is the Transport protocol).
+ *
+ * RFC2960 Section 7.1 SCTP Differences from TCP Congestion control
+ *
+ *   o  The sender keeps a separate congestion control parameter set for
+ *      each of the destination addresses it can send to (not each
+ *      source-destination pair but for each destination).  The parameters
+ *      should decay if the address is not used for a long enough time
+ *      period.
+ *
+ */
+struct sctp_transport {
+	/* A list of transports. */
+	struct list_head transports;
+
+	/* Reference counting. */
+	atomic_t refcnt;
+	int      dead;
+
+	/* This is the peer's IP address and port. */
+	union sctp_addr ipaddr;
+
+	/* These are the functions we call to handle LLP stuff.  */
+	struct sctp_af *af_specific;
+
+	/* Which association do we belong to?  */
+	struct sctp_association *asoc;
+
+	/* RFC2960
+	 *
+	 * 12.3 Per Transport Address Data
+	 *
+	 * For each destination transport address in the peer's
+	 * address list derived from the INIT or INIT ACK chunk, a
+	 * number of data elements needs to be maintained including:
+	 */
+	__u32 rtt;		/* This is the most recent RTT.  */
+
+	/* RTO         : The current retransmission timeout value.  */
+	__u32 rto;
+
+	/* RTTVAR      : The current RTT variation.  */
+	__u32 rttvar;
+
+	/* SRTT        : The current smoothed round trip time.  */
+	__u32 srtt;
+
+	/* RTO-Pending : A flag used to track if one of the DATA
+	 *              chunks sent to this address is currently being
+	 *              used to compute a RTT. If this flag is 0,
+	 *              the next DATA chunk sent to this destination
+	 *              should be used to compute a RTT and this flag
+	 *              should be set. Every time the RTT
+	 *              calculation completes (i.e. the DATA chunk
+	 *              is SACK'd) clear this flag.
+         */
+	int rto_pending;
+
+	/*
+	 * These are the congestion stats.
+	 */
+	/* cwnd        : The current congestion window.  */
+	__u32 cwnd;               /* This is the actual cwnd.  */
+
+	/* ssthresh    : The current slow start threshold value.  */
+	__u32 ssthresh;
+
+	/* partial     : The tracking method for increase of cwnd when in
+	 * bytes acked : congestion avoidance mode (see Section 6.2.2)
+	 */
+	__u32 partial_bytes_acked;
+
+	/* Data that has been sent, but not acknowledged. */
+	__u32 flight_size;
+
+	/* PMTU       : The current known path MTU.  */
+	__u32 pmtu;
+
+	/* Destination */
+	struct dst_entry *dst;
+	/* Source address. */
+	union sctp_addr saddr;
+
+	/* When was the last time(in jiffies) that a data packet was sent on
+	 * this transport?  This is used to adjust the cwnd when the transport
+	 * becomes inactive.
+	 */
+	unsigned long last_time_used;
+
+	/* Heartbeat interval: The endpoint sends out a Heartbeat chunk to
+	 * the destination address every heartbeat interval.
+	 */
+	int hb_interval;
+
+	/* When was the last time (in jiffies) that we heard from this
+	 * transport?  We use this to pick new active and retran paths.
+	 */
+	unsigned long last_time_heard;
+
+	/* Last time(in jiffies) when cwnd is reduced due to the congestion
+	 * indication based on ECNE chunk.
+	 */
+	unsigned long last_time_ecne_reduced;
+
+	/* active      : The current active state of this destination,
+	 *             :  i.e. DOWN, UP, etc.
+	 */
+	int active;
+
+	/* hb_allowed  : The current heartbeat state of this destination,
+	 *             :  i.e. ALLOW-HB, NO-HEARTBEAT, etc.
+	 */
+	int hb_allowed;
+
+	/* These are the error stats for this destination.  */
+
+	/* Error count : The current error count for this destination.  */
+	unsigned short error_count;
+
+	/* Error       : Current error threshold for this destination
+	 * Threshold   : i.e. what value marks the destination down if
+	 *             : errorCount reaches this value.
+	 */
+	unsigned short error_threshold;
+
+	/* This is the max_retrans value for the transport and will
+	 * be initialized to proto.max_retrans.path.  This can be changed
+	 * using SCTP_SET_PEER_ADDR_PARAMS socket option.
+	 */
+	int max_retrans;
+
+	/* We use this name for debugging output... */
+	char *debug_name;
+
+	/* Per         : A timer used by each destination.
+	 * Destination :
+	 * Timer       :
+	 *
+	 * [Everywhere else in the text this is called T3-rtx. -ed]
+	 */
+	struct timer_list T3_rtx_timer;
+
+	/* Heartbeat timer is per destination. */
+	struct timer_list hb_timer;
+
+	/* Since we're using per-destination retransmission timers
+	 * (see above), we're also using per-destination "transmitted"
+	 * queues.  This probably ought to be a private struct
+	 * accessible only within the outqueue, but it's not, yet.
+	 */
+	struct list_head transmitted;
+
+	/* We build bundle-able packets for this transport here.  */
+	struct sctp_packet packet;
+
+	/* This is the list of transports that have chunks to send.  */
+	struct list_head send_ready;
+
+	int malloced; /* Is this structure kfree()able? */
+};
+
+struct sctp_transport *sctp_transport_new(const union sctp_addr *, int);
+struct sctp_transport *sctp_transport_init(struct sctp_transport *,
+					   const union sctp_addr *, int);
+void sctp_transport_set_owner(struct sctp_transport *,
+			      struct sctp_association *);
+void sctp_transport_route(struct sctp_transport *, union sctp_addr *,
+			  struct sctp_opt *);
+void sctp_transport_pmtu(struct sctp_transport *);
+void sctp_transport_free(struct sctp_transport *);
+void sctp_transport_destroy(struct sctp_transport *);
+void sctp_transport_reset_timers(struct sctp_transport *);
+void sctp_transport_hold(struct sctp_transport *);
+void sctp_transport_put(struct sctp_transport *);
+void sctp_transport_update_rto(struct sctp_transport *, __u32);
+void sctp_transport_raise_cwnd(struct sctp_transport *, __u32, __u32);
+void sctp_transport_lower_cwnd(struct sctp_transport *, sctp_lower_cwnd_t);
+unsigned long sctp_transport_timeout(struct sctp_transport *);
+
+
+/* This is the structure we use to queue packets as they come into
+ * SCTP.  We write packets to it and read chunks from it.
+ */
+struct sctp_inq {
+	/* This is actually a queue of sctp_chunk each
+	 * containing a partially decoded packet.
+	 */
+	struct sk_buff_head in;
+	/* This is the packet which is currently off the in queue and is
+	 * being worked on through the inbound chunk processing.
+	 */
+	sctp_chunk_t *in_progress;
+
+	/* This is the delayed task to finish delivering inbound
+	 * messages.
+	 */
+	struct tq_struct immediate;
+
+	int malloced;        /* Is this structure kfree()able?  */
+};
+
+struct sctp_inq *sctp_inq_new(void);
+void sctp_inq_init(struct sctp_inq *);
+void sctp_inq_free(struct sctp_inq *);
+void sctp_inq_push(struct sctp_inq *, sctp_chunk_t *packet);
+struct sctp_chunk *sctp_inq_pop(struct sctp_inq *);
+void sctp_inq_set_th_handler(struct sctp_inq *, void (*)(void *), void *);
+
+/* This is the structure we use to hold outbound chunks.  You push
+ * chunks in and they automatically pop out the other end as bundled
+ * packets (it calls (*output_handler)()).
+ *
+ * This structure covers sections 6.3, 6.4, 6.7, 6.8, 6.10, 7., 8.1,
+ * and 8.2 of the v13 draft.
+ *
+ * It handles retransmissions.  The connection to the timeout portion
+ * of the state machine is through sctp_..._timeout() and timeout_handler.
+ *
+ * If you feed it SACKs, it will eat them.
+ *
+ * If you give it big chunks, it will fragment them.
+ *
+ * It assigns TSN's to data chunks.  This happens at the last possible
+ * instant before transmission.
+ *
+ * When free()'d, it empties itself out via output_handler().
+ */
+struct sctp_outq {
+	struct sctp_association *asoc;
+
+	/* Data pending that has never been transmitted.  */
+	struct sk_buff_head out;
+
+	unsigned out_qlen;	/* Total length of queued data chunks. */
+
+	/* Error of send failed, may used in SCTP_SEND_FAILED event. */
+	unsigned error;
+
+	/* These are control chunks we want to send.  */
+	struct sk_buff_head control;
+
+	/* These are chunks that have been sacked but are above the
+	 * CTSN, or cumulative tsn ack point.
+	 */
+	struct list_head sacked;
+
+	/* Put chunks on this list to schedule them for
+	 * retransmission.
+	 */
+	struct list_head retransmit;
+
+	/* Call these functions to send chunks down to the next lower
+	 * layer.  This is always sctp_packet, but we separate the two
+	 * structures to make testing simpler.
+	 */
+	sctp_outq_ohandler_init_t	*init_output;
+	sctp_outq_ohandler_config_t	*config_output;
+	sctp_outq_ohandler_t	*append_output;
+	sctp_outq_ohandler_t	*build_output;
+	sctp_outq_ohandler_force_t	*force_output;
+
+	/* How many unackd bytes do we have in-flight?  */
+	__u32 outstanding_bytes;
+
+	/* Is this structure empty?  */
+	int empty;
+
+	/* Are we kfree()able? */
+	int malloced;
+};
+
+struct sctp_outq *sctp_outq_new(struct sctp_association *);
+void sctp_outq_init(struct sctp_association *, struct sctp_outq *);
+void sctp_outq_teardown(struct sctp_outq *);
+void sctp_outq_free(struct sctp_outq*);
+int sctp_outq_tail(struct sctp_outq *, sctp_chunk_t *chunk);
+int sctp_outq_flush(struct sctp_outq *, int);
+int sctp_outq_sack(struct sctp_outq *, sctp_sackhdr_t *);
+int sctp_outq_is_empty(const struct sctp_outq *);
+int sctp_outq_set_output_handlers(struct sctp_outq *,
+				  sctp_outq_ohandler_init_t init,
+				  sctp_outq_ohandler_config_t config,
+				  sctp_outq_ohandler_t append,
+				  sctp_outq_ohandler_t build,
+				  sctp_outq_ohandler_force_t force);
+void sctp_outq_restart(struct sctp_outq *);
+void sctp_retransmit(struct sctp_outq *, struct sctp_transport *,
+		     sctp_retransmit_reason_t);
+void sctp_retransmit_mark(struct sctp_outq *, struct sctp_transport *, __u8);
+
+
+/* These bind address data fields common between endpoints and associations */
+struct sctp_bind_addr {
+
+	/* RFC 2960 12.1 Parameters necessary for the SCTP instance
+	 *
+	 * SCTP Port:   The local SCTP port number the endpoint is
+	 * 		bound to.
+	 */
+	__u16 port;
+
+	/* RFC 2960 12.1 Parameters necessary for the SCTP instance
+	 *
+	 * Address List: The list of IP addresses that this instance
+	 *	has bound.  This information is passed to one's
+	 *	peer(s) in INIT and INIT ACK chunks.
+	 */
+	struct list_head address_list;
+
+	int malloced;        /* Are we kfree()able?  */
+};
+
+sctp_bind_addr_t *sctp_bind_addr_new(int gfp_mask);
+void sctp_bind_addr_init(sctp_bind_addr_t *, __u16 port);
+void sctp_bind_addr_free(sctp_bind_addr_t *);
+int sctp_bind_addr_copy(sctp_bind_addr_t *dest, const sctp_bind_addr_t *src,
+			sctp_scope_t scope, int gfp,int flags);
+int sctp_add_bind_addr(sctp_bind_addr_t *, union sctp_addr *,
+		       int gfp);
+int sctp_del_bind_addr(sctp_bind_addr_t *, union sctp_addr *);
+int sctp_bind_addr_match(sctp_bind_addr_t *, const union sctp_addr *,
+			 struct sctp_opt *);
+union sctp_params sctp_bind_addrs_to_raw(const struct sctp_bind_addr *bp,
+					 int *addrs_len, int gfp);
+int sctp_raw_to_bind_addrs(struct sctp_bind_addr *bp, __u8 *raw, int len,
+			   __u16 port, int gfp);
+
+sctp_scope_t sctp_scope(const union sctp_addr *);
+int sctp_in_scope(const union sctp_addr *addr, const sctp_scope_t scope);
+int sctp_is_any(const union sctp_addr *addr);
+int sctp_addr_is_valid(const union sctp_addr *addr);
+
+
+/* What type of endpoint?  */
+typedef enum {
+	SCTP_EP_TYPE_SOCKET,
+	SCTP_EP_TYPE_ASSOCIATION,
+} sctp_endpoint_type_t;
+
+/*
+ * A common base class to bridge the implmentation view of a
+ * socket (usually listening) endpoint versus an association's
+ * local endpoint.
+ * This common structure is useful for several purposes:
+ *   1) Common interface for lookup routines.
+ *      a) Subfunctions work for either endpoint or association
+ *      b) Single interface to lookup allows hiding the lookup lock rather
+ *         than acquiring it externally.
+ *   2) Common interface for the inbound chunk handling/state machine.
+ *   3) Common object handling routines for reference counting, etc.
+ *   4) Disentangle association lookup from endpoint lookup, where we
+ *      do not have to find our endpoint to find our association.
+ *
+ */
+
+struct sctp_ep_common {
+	/* Fields to help us manage our entries in the hash tables. */
+	struct sctp_ep_common *next;
+	struct sctp_ep_common **pprev;
+	int hashent;
+
+	/* Runtime type information.  What kind of endpoint is this? */
+	sctp_endpoint_type_t type;
+
+	/* Some fields to help us manage this object.
+	 *   refcnt   - Reference count access to this object.
+	 *   dead     - Do not attempt to use this object.
+	 *   malloced - Do we need to kfree this object?
+	 */
+	atomic_t    refcnt;
+	char        dead;
+	char        malloced;
+
+	/* What socket does this endpoint belong to?  */
+	struct sock *sk;
+
+	/* This is where we receive inbound chunks.  */
+	struct sctp_inq   inqueue;
+
+	/* This substructure includes the defining parameters of the
+	 * endpoint:
+	 * bind_addr.port is our shared port number.
+	 * bind_addr.address_list is our set of local IP addresses.
+	 */
+	sctp_bind_addr_t bind_addr;
+
+	/* Protection during address list comparisons. */
+	rwlock_t   addr_lock;
+};
+
+
+/* RFC Section 1.4 Key Terms
+ *
+ * o SCTP endpoint: The logical sender/receiver of SCTP packets. On a
+ *   multi-homed host, an SCTP endpoint is represented to its peers as a
+ *   combination of a set of eligible destination transport addresses to
+ *   which SCTP packets can be sent and a set of eligible source
+ *   transport addresses from which SCTP packets can be received.
+ *   All transport addresses used by an SCTP endpoint must use the
+ *   same port number, but can use multiple IP addresses. A transport
+ *   address used by an SCTP endpoint must not be used by another
+ *   SCTP endpoint. In other words, a transport address is unique
+ *   to an SCTP endpoint.
+ *
+ * From an implementation perspective, each socket has one of these.
+ * A TCP-style socket will have exactly one association on one of
+ * these.  An UDP-style socket will have multiple associations hanging
+ * off one of these.
+ */
+
+struct sctp_endpoint {
+	/* Common substructure for endpoint and association. */
+	struct sctp_ep_common base;
+
+	/* Associations: A list of current associations and mappings
+	 *            to the data consumers for each association. This
+	 *            may be in the form of a hash table or other
+	 *            implementation dependent structure. The data
+	 *            consumers may be process identification
+	 *            information such as file descriptors, named pipe
+	 *            pointer, or table pointers dependent on how SCTP
+	 *            is implemented.
+	 */
+	/* This is really a list of struct sctp_association entries. */
+	struct list_head asocs;
+
+	/* Secret Key: A secret key used by this endpoint to compute
+	 *            the MAC.  This SHOULD be a cryptographic quality
+	 *            random number with a sufficient length.
+	 *	      Discussion in [RFC1750] can be helpful in
+	 * 	      selection of the key.
+	 */
+	__u8 secret_key[SCTP_HOW_MANY_SECRETS][SCTP_SECRET_SIZE];
+	int current_key;
+	int last_key;
+	int key_changed_at;
+
+	/* Default timeouts.  */
+	int timeouts[SCTP_NUM_TIMEOUT_TYPES];
+
+	/* Various thresholds.  */
+
+	/* Name for debugging output... */
+	char *debug_name;
+};
+
+/* Recover the outter endpoint structure. */
+static inline struct sctp_endpoint *sctp_ep(struct sctp_ep_common *base)
+{
+	struct sctp_endpoint *ep;
+
+	ep = container_of(base, struct sctp_endpoint, base);
+	return ep;
+}
+
+/* These are function signatures for manipulating endpoints.  */
+struct sctp_endpoint *sctp_endpoint_new(struct sock *, int);
+struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *,
+					 struct sock *, int gfp);
+void sctp_endpoint_free(struct sctp_endpoint *);
+void sctp_endpoint_put(struct sctp_endpoint *);
+void sctp_endpoint_hold(struct sctp_endpoint *);
+void sctp_endpoint_add_asoc(struct sctp_endpoint *, struct sctp_association *);
+struct sctp_association *sctp_endpoint_lookup_assoc(
+	const struct sctp_endpoint *ep,
+	const union sctp_addr *paddr,
+	struct sctp_transport **);
+int sctp_endpoint_is_peeled_off(struct sctp_endpoint *,
+				const union sctp_addr *);
+struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *,
+					const union sctp_addr *);
+int sctp_has_association(const union sctp_addr *laddr,
+			 const union sctp_addr *paddr);
+
+int sctp_verify_init(const struct sctp_association *asoc, sctp_cid_t,
+		     sctp_init_chunk_t *peer_init, struct sctp_chunk *chunk,
+		     struct sctp_chunk **err_chunk);
+int sctp_process_init(struct sctp_association *, sctp_cid_t cid,
+		      const union sctp_addr *peer,
+		      sctp_init_chunk_t *init, int gfp);
+int sctp_process_param(struct sctp_association *, union sctp_params param,
+		       const union sctp_addr *from, int gfp);
+__u32 sctp_generate_tag(const struct sctp_endpoint *);
+__u32 sctp_generate_tsn(const struct sctp_endpoint *);
+
+
+/* RFC2960
+ *
+ * 12. Recommended Transmission Control Block (TCB) Parameters
+ *
+ * This section details a recommended set of parameters that should
+ * be contained within the TCB for an implementation. This section is
+ * for illustrative purposes and should not be deemed as requirements
+ * on an implementation or as an exhaustive list of all parameters
+ * inside an SCTP TCB. Each implementation may need its own additional
+ * parameters for optimization.
+ */
+
+
+/* Here we have information about each individual association. */
+struct sctp_association {
+
+	/* A base structure common to endpoint and association.
+	 * In this context, it represents the associations's view
+	 * of the local endpoint of the association.
+	 */
+	struct sctp_ep_common base;
+
+	/* Associations on the same socket. */
+	struct list_head asocs;
+
+	/* This is a signature that lets us know that this is a
+	 * struct sctp_association data structure.  Used for mapping an
+	 * association id to an association.
+	 */
+	__u32 eyecatcher;
+
+	/* This is our parent endpoint.  */
+	struct sctp_endpoint *ep;
+
+	/* These are those association elements needed in the cookie.  */
+	sctp_cookie_t c;
+
+	/* This is all information about our peer.  */
+	struct {
+		/* rwnd
+		 *
+		 * Peer Rwnd   : Current calculated value of the peer's rwnd.
+		 */
+		__u32 rwnd;
+
+		/* transport_addr_list
+		 *
+		 * Peer        : A list of SCTP transport addresses that the
+		 * Transport   : peer is bound to. This information is derived
+		 * Address     : from the INIT or INIT ACK and is used to
+		 * List        : associate an inbound packet with a given
+		 *             : association. Normally this information is
+		 *	       : hashed or keyed for quick lookup and access
+		 *	       : of the TCB.
+		 *
+		 * It is a list of SCTP_transport's.
+		 */
+		struct list_head transport_addr_list;
+
+		/* port
+		 *   The transport layer port number.
+		 */
+		__u16 port;
+
+		/* primary_path
+		 *
+		 * Primary     : This is the current primary destination
+		 * Path        : transport address of the peer endpoint.  It
+		 *             : may also specify a source transport address
+		 *	       : on this endpoint.
+		 *
+		 * All of these paths live on transport_addr_list.
+		 *
+		 * At the bakeoffs, we discovered that the intent of
+		 * primaryPath is that it only changes when the ULP
+		 * asks to have it changed.  We add the activePath to
+		 * designate the connection we are currently using to
+		 * transmit new data and most control chunks.
+		 */
+		struct sctp_transport *primary_path;
+
+		/* Cache the primary path address here, when we
+		 * need a an address for msg_name.
+		 */
+		union sctp_addr primary_addr;
+
+		/* active_path
+		 *   The path that we are currently using to
+		 *   transmit new data and most control chunks.
+		 */
+		struct sctp_transport *active_path;
+
+		/* retran_path
+		 *
+		 * RFC2960 6.4 Multi-homed SCTP Endpoints
+		 * ...
+		 * Furthermore, when its peer is multi-homed, an
+		 * endpoint SHOULD try to retransmit a chunk to an
+		 * active destination transport address that is
+		 * different from the last destination address to
+		 * which the DATA chunk was sent.
+		 */
+		struct sctp_transport *retran_path;
+
+		/* Pointer to last transport I have sent on.  */
+		struct sctp_transport *last_sent_to;
+
+		/* This is the last transport I have received DATA on.  */
+		struct sctp_transport *last_data_from;
+
+		/*
+		 * Mapping  An array of bits or bytes indicating which out of
+		 * Array    order TSN's have been received (relative to the
+		 *          Last Rcvd TSN). If no gaps exist, i.e. no out of
+		 *          order packets have been received, this array
+		 *          will be set to all zero. This structure may be
+		 *          in the form of a circular buffer or bit array.
+		 *
+		 * Last Rcvd   : This is the last TSN received in
+		 * TSN	       : sequence. This value is set initially by
+		 *             : taking the peer's Initial TSN, received in
+		 *             : the INIT or INIT ACK chunk, and subtracting
+		 *             : one from it.
+		 *
+		 * Throughout most of the specification this is called the
+		 * "Cumulative TSN ACK Point".  In this case, we
+		 * ignore the advice in 12.2 in favour of the term
+		 * used in the bulk of the text.  This value is hidden
+		 * in tsn_map--we get it by calling sctp_tsnmap_get_ctsn().
+		 */
+		struct sctp_tsnmap tsn_map;
+		__u8 _map[sctp_tsnmap_storage_size(SCTP_TSN_MAP_SIZE)];
+
+		/* Do we need to sack the peer? */
+		__u8    sack_needed;
+		/* These are capabilities which our peer advertised.  */
+		__u8	ecn_capable;     /* Can peer do ECN? */
+		__u8	ipv4_address;    /* Peer understands IPv4 addresses? */
+		__u8	ipv6_address;    /* Peer understands IPv6 addresses? */
+		__u8	hostname_address;/* Peer understands DNS addresses? */
+		sctp_inithdr_t i;
+		int cookie_len;
+		void *cookie;
+
+		/* ADDIP Extention (ADDIP)		--xguo */
+		/* <expected peer-serial-number> minus 1 (ADDIP sec. 4.2 C1) */
+		__u32 addip_serial;
+	} peer;
+
+	/* State       : A state variable indicating what state the
+	 *	       : association is in, i.e. COOKIE-WAIT,
+	 *             : COOKIE-ECHOED, ESTABLISHED, SHUTDOWN-PENDING,
+	 *             : SHUTDOWN-SENT, SHUTDOWN-RECEIVED, SHUTDOWN-ACK-SENT.
+	 *
+	 *              Note: No "CLOSED" state is illustrated since if a
+	 *              association is "CLOSED" its TCB SHOULD be removed.
+	 *
+	 * 		In this implementation we DO have a CLOSED
+	 *		state which is used during initiation and shutdown.
+	 *
+	 * 		State takes values from SCTP_STATE_*.
+	 */
+	sctp_state_t state;
+
+	/* When did we enter this state?  */
+	int state_timestamp;
+
+	/* The cookie life I award for any cookie.  */
+	struct timeval cookie_life;
+
+	/* Overall     : The overall association error count.
+	 * Error Count : [Clear this any time I get something.]
+	 */
+	int overall_error_count;
+
+	/* Overall     : The threshold for this association that if
+	 * Error       : the Overall Error Count reaches will cause
+	 * Threshold   : this association to be torn down.
+	 */
+	int overall_error_threshold;
+
+	/* These are the association's initial, max, and min RTO values.
+	 * These values will be initialized by system defaults, but can
+	 * be modified via the SCTP_RTOINFO socket option.
+	 */
+	__u32 rto_initial;
+	__u32 rto_max;
+	__u32 rto_min;
+
+	/* Maximum number of new data packets that can be sent in a burst.  */
+	int max_burst;
+
+	/* This is the max_retrans value for the association.  This value will
+	 * be initialized initialized from system defaults, but can be
+	 * modified by the SCTP_ASSOCINFO socket option.
+	 */
+	int max_retrans;
+
+	/* Maximum number of times the endpoint will retransmit INIT  */
+	__u16 max_init_attempts;
+
+	/* How many times have we resent an INIT? */
+	__u16 init_retries;
+
+	/* The largest timeout or RTO value to use in attempting an INIT */
+	__u16 max_init_timeo;
+
+
+	int timeouts[SCTP_NUM_TIMEOUT_TYPES];
+	struct timer_list timers[SCTP_NUM_TIMEOUT_TYPES];
+
+	/* Transport to which SHUTDOWN chunk was last sent.  */
+	struct sctp_transport *shutdown_last_sent_to;
+
+	/* Next TSN    : The next TSN number to be assigned to a new
+	 *             : DATA chunk.  This is sent in the INIT or INIT
+	 *             : ACK chunk to the peer and incremented each
+	 *             : time a DATA chunk is assigned a TSN
+	 *             : (normally just prior to transmit or during
+	 *	       : fragmentation).
+	 */
+	__u32 next_tsn;
+
+	/*
+	 * Last Rcvd   : This is the last TSN received in sequence.  This value
+	 * TSN         : is set initially by taking the peer's Initial TSN,
+	 *             : received in the INIT or INIT ACK chunk, and
+	 *             : subtracting one from it.
+	 *
+	 * Most of RFC 2960 refers to this as the Cumulative TSN Ack Point.
+	 */
+
+	__u32 ctsn_ack_point;
+
+	/* Highest TSN that is acknowledged by incoming SACKs. */
+	__u32 highest_sacked;
+
+	/* The number of unacknowledged data chunks.  Reported through
+	 * the SCTP_STATUS sockopt.
+	 */
+	__u16 unack_data;
+
+	/* This is the association's receive buffer space.  This value is used
+	 * to set a_rwnd field in an INIT or a SACK chunk.
+	 */
+	__u32 rwnd;
+
+	/* This is the last advertised value of rwnd over a SACK chunk. */
+	__u32 a_rwnd;
+
+	/* Number of bytes by which the rwnd has slopped.  The rwnd is allowed
+	 * to slop over a maximum of the association's frag_point.
+	 */
+	__u32 rwnd_over;
+
+	/* This is the sndbuf size in use for the association.
+	 * This corresponds to the sndbuf size for the association,
+	 * as specified in the sk->sndbuf.
+	 */
+	int sndbuf_used;
+
+	/* This is the wait queue head for send requests waiting on
+	 * the association sndbuf space.
+	 */
+	wait_queue_head_t	wait;
+
+	/* Association : The smallest PMTU discovered for all of the
+	 * PMTU        : peer's transport addresses.
+	 */
+	__u32 pmtu;
+
+	/* The message size at which SCTP fragmentation will occur. */
+	__u32 frag_point;
+
+	/* Ack State   : This flag indicates if the next received
+	 *             : packet is to be responded to with a
+	 *             : SACK. This is initializedto 0.  When a packet
+	 *             : is received it is incremented. If this value
+	 *             : reaches 2 or more, a SACK is sent and the
+	 *             : value is reset to 0. Note: This is used only
+	 *             : when no DATA chunks are received out of
+	 *	       : order.  When DATA chunks are out of order,
+	 *             : SACK's are not delayed (see Section 6).
+	 */
+	/* Do we need to send an ack?
+	 * When counters[SctpCounterAckState] is above 1 we do!
+	 */
+	int counters[SCTP_NUMBER_COUNTERS];
+
+	struct {
+		__u16 stream;
+		__u16 flags;
+		__u32 ppid;
+		__u32 context;
+		__u32 timetolive;
+	} defaults;
+
+	/* This tracks outbound ssn for a given stream.  */
+	struct sctp_ssnmap *ssnmap;
+
+	/* All outbound chunks go through this structure.  */
+	struct sctp_outq outqueue;
+
+	/* A smart pipe that will handle reordering and fragmentation,
+	 * as well as handle passing events up to the ULP.
+	 */
+	struct sctp_ulpq ulpq;
+
+	/* Need to send an ECNE Chunk? */
+	int need_ecne;
+
+	/* Last TSN that caused an ECNE Chunk to be sent.  */
+	__u32 last_ecne_tsn;
+
+	/* Last TSN that caused a CWR Chunk to be sent.  */
+	__u32 last_cwr_tsn;
+
+	/* How many duplicated TSNs have we seen?  */
+	int numduptsns;
+
+	/* Number of seconds of idle time before an association is closed.  */
+	__u32 autoclose;
+
+	/* Name for debugging output... */
+	char *debug_name;
+
+	/* These are to support
+	 * "SCTP Extensions for Dynamic Reconfiguration of IP Addresses
+	 *  and Enforcement of Flow and Message Limits"
+	 * <draft-ietf-tsvwg-addip-sctp-02.txt>
+	 * or "ADDIP" for short.
+	 */
+
+	/* Is the ADDIP extension enabled for this association? */
+	int addip_enable;
+
+	/* ADDIP Section 4.1.1 Congestion Control of ASCONF Chunks
+	 *
+	 * R1) One and only one ASCONF Chunk MAY be in transit and
+	 * unacknowledged at any one time.  If a sender, after sending
+	 * an ASCONF chunk, decides it needs to transfer another
+	 * ASCONF Chunk, it MUST wait until the ASCONF-ACK Chunk
+	 * returns from the previous ASCONF Chunk before sending a
+	 * subsequent ASCONF. Note this restriction binds each side,
+	 * so at any time two ASCONF may be in-transit on any given
+	 * association (one sent from each endpoint).
+	 *
+	 * [This is our one-and-only-one ASCONF in flight.  If we do
+	 * not have an ASCONF in flight, this is NULL.]
+	 */
+	sctp_chunk_t *addip_last_asconf;
+
+	/* ADDIP Section 4.2 Upon reception of an ASCONF Chunk.
+	 *
+	 * IMPLEMENTATION NOTE: As an optimization a receiver may wish
+	 * to save the last ASCONF-ACK for some predetermined period
+	 * of time and instead of re-processing the ASCONF (with the
+	 * same serial number) it may just re-transmit the
+	 * ASCONF-ACK. It may wish to use the arrival of a new serial
+	 * number to discard the previously saved ASCONF-ACK or any
+	 * other means it may choose to expire the saved ASCONF-ACK.
+	 *
+	 * [This is our saved ASCONF-ACK.  We invalidate it when a new
+	 * ASCONF serial number arrives.]
+	 */
+	sctp_chunk_t *addip_last_asconf_ack;
+
+	/* These ASCONF chunks are waiting to be sent.
+	 *
+	 * These chunaks can't be pushed to outqueue until receiving
+	 * ASCONF_ACK for the previous ASCONF indicated by
+	 * addip_last_asconf, so as to guarantee that only one ASCONF
+	 * is in flight at any time.
+	 *
+	 * ADDIP Section 4.1.1 Congestion Control of ASCONF Chunks
+	 *
+	 * In defining the ASCONF Chunk transfer procedures, it is
+	 * essential that these transfers MUST NOT cause congestion
+	 * within the network.  To achieve this, we place these
+	 * restrictions on the transfer of ASCONF Chunks:
+	 *
+	 * R1) One and only one ASCONF Chunk MAY be in transit and
+	 * unacknowledged at any one time.  If a sender, after sending
+	 * an ASCONF chunk, decides it needs to transfer another
+	 * ASCONF Chunk, it MUST wait until the ASCONF-ACK Chunk
+	 * returns from the previous ASCONF Chunk before sending a
+	 * subsequent ASCONF. Note this restriction binds each side,
+	 * so at any time two ASCONF may be in-transit on any given
+	 * association (one sent from each endpoint).
+	 *
+	 *
+	 * [I really think this is EXACTLY the sort of intelligence
+	 *  which already resides in sctp_outq.  Please move this
+	 *  queue and its supporting logic down there.  --piggy]
+	 */
+	struct sk_buff_head addip_chunks;
+
+	/* ADDIP Section 4.1 ASCONF Chunk Procedures
+	 *
+	 * A2) A serial number should be assigned to the Chunk. The
+	 * serial number should be a monotonically increasing
+	 * number. All serial numbers are defined to be initialized at
+	 * the start of the association to the same value as the
+	 * Initial TSN.
+	 *
+	 * [and]
+	 *
+	 * ADDIP
+	 * 3.1.1  Address/Stream Configuration Change Chunk (ASCONF)
+	 *
+	 * Serial Number : 32 bits (unsigned integer)
+	 *
+	 * This value represents a Serial Number for the ASCONF
+	 * Chunk. The valid range of Serial Number is from 0 to
+	 * 4294967295 (2**32 - 1).  Serial Numbers wrap back to 0
+	 * after reaching 4294967295.
+	 */
+	__u32 addip_serial;
+
+	/* Is it a temporary association? */ 
+	__u8 temp;
+};
+
+
+/* An eyecatcher for determining if we are really looking at an
+ * association data structure.
+ */
+enum {
+	SCTP_ASSOC_EYECATCHER = 0xa550c123,
+};
+
+/* Recover the outter association structure. */
+static inline struct sctp_association *sctp_assoc(struct sctp_ep_common *base)
+{
+	struct sctp_association *asoc;
+
+	asoc = container_of(base, struct sctp_association, base);
+	return asoc;
+}
+
+/* These are function signatures for manipulating associations.  */
+
+
+struct sctp_association *
+sctp_association_new(const struct sctp_endpoint *, const struct sock *,
+		     sctp_scope_t scope, int gfp);
+struct sctp_association *
+sctp_association_init(struct sctp_association *, const struct sctp_endpoint *,
+		      const struct sock *, sctp_scope_t scope,
+		      int gfp);
+void sctp_association_free(struct sctp_association *);
+void sctp_association_put(struct sctp_association *);
+void sctp_association_hold(struct sctp_association *);
+
+struct sctp_transport *sctp_assoc_choose_shutdown_transport(
+	struct sctp_association *);
+void sctp_assoc_update_retran_path(struct sctp_association *);
+struct sctp_transport *sctp_assoc_lookup_paddr(const struct sctp_association *,
+					  const union sctp_addr *);
+struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *,
+				     const union sctp_addr *address,
+				     const int gfp);
+void sctp_assoc_control_transport(struct sctp_association *,
+				  struct sctp_transport *,
+				  sctp_transport_cmd_t, sctp_sn_error_t);
+struct sctp_transport *sctp_assoc_lookup_tsn(struct sctp_association *, __u32);
+struct sctp_transport *sctp_assoc_is_match(struct sctp_association *,
+					   const union sctp_addr *,
+					   const union sctp_addr *);
+void sctp_assoc_migrate(struct sctp_association *, struct sock *);
+void sctp_assoc_update(struct sctp_association *old,
+		       struct sctp_association *new);
+
+__u32 sctp_association_get_next_tsn(struct sctp_association *);
+__u32 sctp_association_get_tsn_block(struct sctp_association *, int);
+
+void sctp_assoc_sync_pmtu(struct sctp_association *);
+void sctp_assoc_rwnd_increase(struct sctp_association *, int);
+void sctp_assoc_rwnd_decrease(struct sctp_association *, int);
+void sctp_assoc_set_primary(struct sctp_association *,
+			    struct sctp_transport *);
+int sctp_assoc_set_bind_addr_from_ep(struct sctp_association *, int);
+int sctp_assoc_set_bind_addr_from_cookie(struct sctp_association *,
+					 sctp_cookie_t *, int gfp);
+
+int sctp_cmp_addr_exact(const union sctp_addr *ss1,
+		        const union sctp_addr *ss2);
+sctp_chunk_t *sctp_get_ecne_prepend(struct sctp_association *asoc);
+sctp_chunk_t *sctp_get_no_prepend(struct sctp_association *asoc);
+
+/* A convenience structure to parse out SCTP specific CMSGs. */
+typedef struct sctp_cmsgs {
+	struct sctp_initmsg *init;
+	struct sctp_sndrcvinfo *info;
+} sctp_cmsgs_t;
+
+/* Structure for tracking memory objects */
+typedef struct {
+	char *label;
+	atomic_t *counter;
+} sctp_dbg_objcnt_entry_t;
+
+#endif /* __sctp_structs_h__ */
diff -urN linux-2.4.23-pre5/include/net/sctp/tsnmap.h linux-2.4.23-pre6/include/net/sctp/tsnmap.h
--- linux-2.4.23-pre5/include/net/sctp/tsnmap.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/include/net/sctp/tsnmap.h	2003-10-01 10:38:57.000000000 -0700
@@ -0,0 +1,186 @@
+/* SCTP kernel reference Implementation Copyright (C) 1999-2001
+ * Cisco, Motorola, Intel, and International Business Machines Corp.
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * These are the definitions needed for the tsnmap type.  The tsnmap is used
+ * to track out of order TSNs received.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *   Jon Grimm             <jgrimm@us.ibm.com>
+ *   La Monte H.P. Yarroll <piggy@acm.org>
+ *   Karl Knutson          <karl@athena.chicago.il.us>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+#include <net/sctp/constants.h>
+
+#ifndef __sctp_tsnmap_h__
+#define __sctp_tsnmap_h__
+
+/* RFC 2960 12.2 Parameters necessary per association (i.e. the TCB)
+ * Mapping  An array of bits or bytes indicating which out of
+ * Array    order TSN's have been received (relative to the
+ *          Last Rcvd TSN). If no gaps exist, i.e. no out of
+ *          order packets have been received, this array
+ *          will be set to all zero. This structure may be
+ *          in the form of a circular buffer or bit array.
+ */
+struct sctp_tsnmap {
+	/* This array counts the number of chunks with each TSN.
+	 * It points at one of the two buffers with which we will
+	 * ping-pong between.
+	 */
+	__u8 *tsn_map;
+
+	/* This marks the tsn which overflows the tsn_map, when the
+	 * cumulative ack point reaches this point we know we can switch
+	 * maps (tsn_map and overflow_map swap).
+	 */
+	__u32 overflow_tsn;
+
+	/* This is the overflow array for tsn_map.
+	 * It points at one of the other ping-pong buffers.
+	 */
+	__u8 *overflow_map;
+
+	/* This is the TSN at tsn_map[0].  */
+	__u32 base_tsn;
+
+	/* Last Rcvd   : This is the last TSN received in
+	 * TSN	       : sequence. This value is set initially by
+	 *             : taking the peer's Initial TSN, received in
+	 *             : the INIT or INIT ACK chunk, and subtracting
+	 *             : one from it.
+	 *
+	 * Throughout most of the specification this is called the
+	 * "Cumulative TSN ACK Point".  In this case, we
+	 * ignore the advice in 12.2 in favour of the term
+	 * used in the bulk of the text.
+	 */
+	__u32 cumulative_tsn_ack_point;
+
+	/* This is the minimum number of TSNs we can track.  This corresponds
+	 * to the size of tsn_map.   Note: the overflow_map allows us to
+	 * potentially track more than this quantity.
+	 */
+	__u16 len;
+
+	/* This is the highest TSN we've marked.  */
+	__u32 max_tsn_seen;
+
+	/* Data chunks pending receipt. used by SCTP_STATUS sockopt */
+	__u16 pending_data;
+
+	/* We record duplicate TSNs here.  We clear this after
+	 * every SACK.  Store up to SCTP_MAX_DUP_TSNS worth of
+	 * information.
+	 */
+	__u32 dup_tsns[SCTP_MAX_DUP_TSNS];
+	__u16 num_dup_tsns;
+
+	int malloced;
+
+	__u8 raw_map[0];
+};
+
+struct sctp_tsnmap_iter {
+	__u32 start;
+};
+
+/* Create a new tsnmap.  */
+struct sctp_tsnmap *sctp_tsnmap_new(__u16 len, __u32 init_tsn, int gfp);
+
+/* Dispose of a tsnmap.  */
+void sctp_tsnmap_free(struct sctp_tsnmap *);
+
+/* This macro assists in creation of external storage for variable length
+ * internal buffers.  We double allocate so the overflow map works.
+ */
+#define sctp_tsnmap_storage_size(count) (sizeof(__u8) * (count) * 2)
+
+/* Initialize a block of memory as a tsnmap.  */
+struct sctp_tsnmap *sctp_tsnmap_init(struct sctp_tsnmap *, __u16 len,
+				     __u32 initial_tsn);
+
+/* Test the tracking state of this TSN.
+ * Returns:
+ *   0 if the TSN has not yet been seen
+ *  >0 if the TSN has been seen (duplicate)
+ *  <0 if the TSN is invalid (too large to track)
+ */
+int sctp_tsnmap_check(const struct sctp_tsnmap *, __u32 tsn);
+
+/* Mark this TSN as seen.  */
+void sctp_tsnmap_mark(struct sctp_tsnmap *, __u32 tsn);
+
+/* Retrieve the Cumulative TSN ACK Point.  */
+__u32 sctp_tsnmap_get_ctsn(const struct sctp_tsnmap *);
+
+/* Retrieve the highest TSN we've seen.  */
+__u32 sctp_tsnmap_get_max_tsn_seen(const struct sctp_tsnmap *);
+
+/* How many Duplicate TSNs are stored? */
+static inline __u16 sctp_tsnmap_num_dups(struct sctp_tsnmap *map)
+{
+	return map->num_dup_tsns;
+}
+
+/* Return pointer to duplicate tsn array as needed by SACK. */
+static inline __u32 *sctp_tsnmap_get_dups(struct sctp_tsnmap *map)
+{
+	map->num_dup_tsns = 0;
+	return map->dup_tsns;
+}
+
+/* Mark a duplicate TSN.  Note:  limit the storage of duplicate TSN
+ * information.
+ */
+static inline void sctp_tsnmap_mark_dup(struct sctp_tsnmap *map, __u32 tsn)
+{
+	if (map->num_dup_tsns < SCTP_MAX_DUP_TSNS)
+		map->dup_tsns[map->num_dup_tsns++] = htonl(tsn);
+}
+
+/* Renege a TSN that was seen.  */
+void sctp_tsnmap_renege(struct sctp_tsnmap *, __u32 tsn);
+
+/* Is there a gap in the TSN map? */
+int sctp_tsnmap_has_gap(const struct sctp_tsnmap *);
+
+/* Initialize a gap ack block interator from user-provided memory.  */
+void sctp_tsnmap_iter_init(const struct sctp_tsnmap *,
+			   struct sctp_tsnmap_iter *);
+
+/* Get the next gap ack blocks.  We return 0 if there are no more
+ * gap ack blocks.
+ */
+int sctp_tsnmap_next_gap_ack(const struct sctp_tsnmap *,
+	struct sctp_tsnmap_iter *,__u16 *start, __u16 *end);
+
+#endif /* __sctp_tsnmap_h__ */
diff -urN linux-2.4.23-pre5/include/net/sctp/ulpevent.h linux-2.4.23-pre6/include/net/sctp/ulpevent.h
--- linux-2.4.23-pre5/include/net/sctp/ulpevent.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/include/net/sctp/ulpevent.h	2003-10-01 10:38:57.000000000 -0700
@@ -0,0 +1,156 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001 International Business Machines, Corp.
+ * Copyright (c) 2001 Intel Corp.
+ * Copyright (c) 2001 Nokia, Inc.
+ * Copyright (c) 2001 La Monte H.P. Yarroll
+ *
+ * These are the definitions needed for the sctp_ulpevent type.  The
+ * sctp_ulpevent type is used to carry information from the state machine
+ * upwards to the ULP.
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *   Jon Grimm             <jgrimm@us.ibm.com>
+ *   La Monte H.P. Yarroll <piggy@acm.org>
+ *   Karl Knutson          <karl@athena.chicago.il.us>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#ifndef __sctp_ulpevent_h__
+#define __sctp_ulpevent_h__
+
+#include <net/sctp/compat.h>
+
+/* A structure to carry information to the ULP (e.g. Sockets API) */
+/* Warning: This sits inside an skb.cb[] area.  Be very careful of
+ * growing this structure as it is at the maximum limit now.
+ */
+struct sctp_ulpevent {
+	struct sctp_sndrcvinfo sndrcvinfo;
+	int msg_flags;
+	int iif;
+};
+#define event_asoc sndrcvinfo.sinfo_assoc_id
+
+/* Retrieve the skb this event sits inside of. */
+static inline struct sk_buff *sctp_event2skb(struct sctp_ulpevent *ev)
+{
+	return container_of((void *)ev, struct sk_buff, cb);
+}
+
+/* Retrieve & cast the event sitting inside the skb. */
+static inline struct sctp_ulpevent *sctp_skb2event(struct sk_buff *skb)
+{
+	return (struct sctp_ulpevent *)skb->cb;
+}
+
+struct sctp_ulpevent *sctp_ulpevent_new(int size, int flags, int gfp);
+struct sctp_ulpevent *sctp_ulpevent_init(struct sctp_ulpevent *, int flags);
+void sctp_ulpevent_free(struct sctp_ulpevent *);
+int sctp_ulpevent_is_notification(const struct sctp_ulpevent *);
+
+struct sctp_ulpevent *sctp_ulpevent_make_assoc_change(
+	const struct sctp_association *asoc,
+	__u16 flags,
+	__u16 state,
+	__u16 error,
+	__u16 outbound,
+	__u16 inbound,
+	int gfp);
+
+struct sctp_ulpevent *sctp_ulpevent_make_peer_addr_change(
+	const struct sctp_association *asoc,
+	const struct sockaddr_storage *aaddr,
+	int flags,
+	int state,
+	int error,
+	int gfp);
+
+struct sctp_ulpevent *sctp_ulpevent_make_remote_error(
+	const struct sctp_association *asoc,
+	struct sctp_chunk *chunk,
+	__u16 flags,
+	int gfp);
+struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
+	const struct sctp_association *asoc,
+	struct sctp_chunk *chunk,
+	__u16 flags,
+	__u32 error,
+	int gfp);
+
+struct sctp_ulpevent *sctp_ulpevent_make_shutdown_event(
+	const struct sctp_association *asoc,
+	__u16 flags,
+	int gfp);
+
+struct sctp_ulpevent *sctp_ulpevent_make_pdapi(
+	const struct sctp_association *asoc,
+	__u32 indication, int gfp);
+
+struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
+	struct sctp_chunk *chunk,
+	int gfp);
+
+void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event,
+	struct msghdr *);
+__u16 sctp_ulpevent_get_notification_type(const struct sctp_ulpevent *event);
+
+/* Is this event type enabled? */
+static inline int sctp_ulpevent_type_enabled(__u16 sn_type,
+					     struct sctp_event_subscribe *mask)
+{
+	char *amask = (char *) mask;
+	return amask[sn_type - SCTP_SN_TYPE_BASE];
+}
+
+/* Given an event subscription, is this event enabled? */
+static inline int sctp_ulpevent_is_enabled(const struct sctp_ulpevent *event,
+					   struct sctp_event_subscribe *mask)
+{
+	__u16 sn_type;
+	int enabled = 1;
+
+	if (sctp_ulpevent_is_notification(event)) {
+		sn_type = sctp_ulpevent_get_notification_type(event);
+		enabled = sctp_ulpevent_type_enabled(sn_type, mask);
+	}
+	return enabled;
+}
+
+#endif /* __sctp_ulpevent_h__ */
+
+
+
+
+
+
+
diff -urN linux-2.4.23-pre5/include/net/sctp/ulpqueue.h linux-2.4.23-pre6/include/net/sctp/ulpqueue.h
--- linux-2.4.23-pre5/include/net/sctp/ulpqueue.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/include/net/sctp/ulpqueue.h	2003-10-01 10:38:57.000000000 -0700
@@ -0,0 +1,89 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001-2003 International Business Machines, Corp.
+ * Copyright (c) 2001 Intel Corp.
+ * Copyright (c) 2001 Nokia, Inc.
+ * Copyright (c) 2001 La Monte H.P. Yarroll
+ *
+ * These are the definitions needed for the sctp_ulpq type.  The
+ * sctp_ulpq is the interface between the Upper Layer Protocol, or ULP,
+ * and the core SCTP state machine.  This is the component which handles
+ * reassembly and ordering.
+ *
+ * The SCTP reference implementation  is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * the SCTP reference implementation  is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email addresses:
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *   Jon Grimm             <jgrimm@us.ibm.com>
+ *   La Monte H.P. Yarroll <piggy@acm.org>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#ifndef __sctp_ulpqueue_h__
+#define __sctp_ulpqueue_h__
+
+/* A structure to carry information to the ULP (e.g. Sockets API) */
+struct sctp_ulpq {
+	char malloced;
+	char pd_mode;
+	struct sctp_association *asoc;
+	struct sk_buff_head reasm;
+	struct sk_buff_head lobby;
+};
+
+/* Prototypes. */
+struct sctp_ulpq *sctp_ulpq_new(struct sctp_association *asoc, int gfp);
+struct sctp_ulpq *sctp_ulpq_init(struct sctp_ulpq *,
+				 struct sctp_association *);
+void sctp_ulpq_free(struct sctp_ulpq *);
+
+/* Add a new DATA chunk for processing. */
+int sctp_ulpq_tail_data(struct sctp_ulpq *, struct sctp_chunk *, int);
+
+/* Add a new event for propagation to the ULP. */
+int sctp_ulpq_tail_event(struct sctp_ulpq *, struct sctp_ulpevent *ev);
+
+/* Renege previously received chunks.  */
+void sctp_ulpq_renege(struct sctp_ulpq *, struct sctp_chunk *, int);
+
+/* Perform partial delivery. */
+void sctp_ulpq_partial_delivery(struct sctp_ulpq *, struct sctp_chunk *, int);
+
+/* Abort the partial delivery. */
+void sctp_ulpq_abort_pd(struct sctp_ulpq *, int);
+
+/* Clear the partial data delivery condition on this socket. */
+int sctp_clear_pd(struct sock *sk);
+
+#endif /* __sctp_ulpqueue_h__ */
+
+
+
+
+
+
+
diff -urN linux-2.4.23-pre5/include/net/sctp/user.h linux-2.4.23-pre6/include/net/sctp/user.h
--- linux-2.4.23-pre5/include/net/sctp/user.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/include/net/sctp/user.h	2003-10-01 10:38:57.000000000 -0700
@@ -0,0 +1,628 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001 International Business Machines, Corp.
+ * 
+ * This file is part of the SCTP kernel reference Implementation
+ * 
+ * This header represents the structures and constants needed to support
+ * the SCTP Extension to the Sockets API. 
+ * 
+ * The SCTP reference implementation is free software; 
+ * you can redistribute it and/or modify it under the terms of 
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ * 
+ * The SCTP reference implementation is distributed in the hope that it 
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.  
+ * 
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ * 
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by: 
+ *    La Monte H.P. Yarroll    <piggy@acm.org>
+ *    R. Stewart               <randall@sctp.chicago.il.us>
+ *    K. Morneau               <kmorneau@cisco.com>
+ *    Q. Xie                   <qxie1@email.mot.com>
+ *    Karl Knutson             <karl@athena.chicago.il.us>
+ *    Jon Grimm                <jgrimm@us.ibm.com>
+ *    Daisy Chang              <daisyc@us.ibm.com>
+ * 
+ * 
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+#include <linux/types.h>
+#include <linux/socket.h>
+
+#ifndef __net_sctp_user_h__
+#define __net_sctp_user_h__
+
+struct sctp_association;
+typedef struct sctp_association * sctp_assoc_t;
+
+/* The following symbols come from the Sockets API Extensions for
+ * SCTP <draft-ietf-tsvwg-sctpsocket-04.txt>.
+ */
+enum sctp_optname {
+	SCTP_RTOINFO,
+#define SCTP_RTOINFO SCTP_RTOINFO
+	SCTP_ASSOCRTXINFO,
+#define SCTP_ASSOCRTXINFO SCTP_ASSOCRTXINFO
+	SCTP_INITMSG,
+#define SCTP_INITMSG SCTP_INITMSG
+	SCTP_AUTO_CLOSE,
+#define SCTP_AUTO_CLOSE SCTP_AUTO_CLOSE
+	SCTP_SET_PRIMARY_ADDR,
+#define SCTP_SET_PRIMARY_ADDR SCTP_SET_PRIMARY_ADDR
+	SCTP_SET_PEER_PRIMARY_ADDR, 
+#define SCTP_SET_PEER_PRIMARY_ADDR SCTP_SET_PEER_PRIMARY_ADDR
+	SCTP_SET_ADAPTATION_LAYER,      
+#define SCTP_SET_ADAPTATION_LAYER SCTP_SET_ADAPTATION_LAYER
+	SCTP_SET_STREAM_TIMEOUTS,
+#define SCTP_SET_STREAM_TIMEOUTS SCTP_SET_STREAM_TIMEOUTS
+	SCTP_DISABLE_FRAGMENTS,
+#define SCTP_DISABLE_FRAGMENTS SCTP_DISABLE_FRAGMENTS
+	SCTP_SET_PEER_ADDR_PARAMS,
+#define SCTP_SET_PEER_ADDR_PARAMS SCTP_SET_PEER_ADDR_PARAMS
+	SCTP_GET_PEER_ADDR_PARAMS,
+#define SCTP_GET_PEER_ADDR_PARAMS SCTP_GET_PEER_ADDR_PARAMS
+	SCTP_STATUS,
+#define SCTP_STATUS SCTP_STATUS
+	SCTP_GET_PEER_ADDR_INFO,
+#define SCTP_GET_PEER_ADDR_INFO SCTP_GET_PEER_ADDR_INFO
+	SCTP_SET_EVENTS,
+#define SCTP_SET_EVENTS SCTP_SET_EVENTS
+	SCTP_AUTOCLOSE,
+#define SCTP_AUTOCLOSE SCTP_AUTOCLOSE
+	SCTP_SET_DEFAULT_SEND_PARAM,
+#define SCTP_SET_DEFAULT_SEND_PARAM SCTP_SET_DEFAULT_SEND_PARAM
+
+	SCTP_SOCKOPT_DEBUG_NAME = 42, /* FIXME */
+#define SCTP_SOCKOPT_DEBUG_NAME	SCTP_SOCKOPT_DEBUG_NAME
+
+	SCTP_SOCKOPT_BINDX_ADD, /* BINDX requests for adding addresses. */
+#define SCTP_SOCKOPT_BINDX_ADD	SCTP_SOCKOPT_BINDX_ADD
+	SCTP_SOCKOPT_BINDX_REM, /* BINDX requests for removing addresses. */
+#define SCTP_SOCKOPT_BINDX_REM	SCTP_SOCKOPT_BINDX_REM
+	SCTP_SOCKOPT_PEELOFF, 	/* peel off association. */
+#define SCTP_SOCKOPT_PEELOFF	SCTP_SOCKOPT_PEELOFF
+	SCTP_GET_PEER_ADDRS_NUM, 	/* Get number of peer addresss. */
+#define SCTP_GET_PEER_ADDRS_NUM	SCTP_GET_PEER_ADDRS_NUM
+	SCTP_GET_PEER_ADDRS, 	/* Get all peer addresss. */
+#define SCTP_GET_PEER_ADDRS	SCTP_GET_PEER_ADDRS
+	SCTP_GET_LOCAL_ADDRS_NUM, 	/* Get number of local addresss. */
+#define SCTP_GET_LOCAL_ADDRS_NUM	SCTP_GET_LOCAL_ADDRS_NUM
+	SCTP_GET_LOCAL_ADDRS, 	/* Get all local addresss. */
+#define SCTP_GET_LOCAL_ADDRS	SCTP_GET_LOCAL_ADDRS
+	SCTP_NODELAY, 	/* Get/set nodelay option. */
+#define SCTP_NODELAY	SCTP_NODELAY
+};
+
+
+/*
+ * 5.2 SCTP msg_control Structures
+ *
+ * A key element of all SCTP-specific socket extensions is the use of
+ * ancillary data to specify and access SCTP-specific data via the
+ * struct msghdr's msg_control member used in sendmsg() and recvmsg().
+ * Fine-grained control over initialization and sending parameters are
+ * handled with ancillary data.
+ *
+ * Each ancillary data item is preceeded by a struct cmsghdr (see
+ * Section 5.1), which defines the function and purpose of the data
+ * contained in in the cmsg_data[] member.
+ */
+
+/*
+ * 5.2.1 SCTP Initiation Structure (SCTP_INIT)
+ *
+ *   This cmsghdr structure provides information for initializing new
+ *   SCTP associations with sendmsg().  The SCTP_INITMSG socket option
+ *   uses this same data structure.  This structure is not used for
+ *   recvmsg().
+ *
+ *   cmsg_level    cmsg_type      cmsg_data[]
+ *   ------------  ------------   ----------------------
+ *   IPPROTO_SCTP  SCTP_INIT      struct sctp_initmsg
+ *
+ */
+struct sctp_initmsg {
+	__u16 sinit_num_ostreams;
+	__u16 sinit_max_instreams;
+	__u16 sinit_max_attempts;
+	__u16 sinit_max_init_timeo;
+};
+
+
+/*
+ * 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
+ *
+ *   This cmsghdr structure specifies SCTP options for sendmsg() and
+ *   describes SCTP header information about a received message through
+ *   recvmsg().
+ *
+ *   cmsg_level    cmsg_type      cmsg_data[]
+ *   ------------  ------------   ----------------------
+ *   IPPROTO_SCTP  SCTP_SNDRCV    struct sctp_sndrcvinfo
+ *
+ */
+struct sctp_sndrcvinfo {
+	__u16 sinfo_stream;
+	__u16 sinfo_ssn;
+	__u16 sinfo_flags;
+	__u32 sinfo_ppid;
+	__u32 sinfo_context;
+	__u32 sinfo_timetolive;
+	__u32 sinfo_tsn;
+	__u32 sinfo_cumtsn;
+	sctp_assoc_t sinfo_assoc_id;
+};
+
+/*
+ *  sinfo_flags: 16 bits (unsigned integer)
+ *
+ *   This field may contain any of the following flags and is composed of
+ *   a bitwise OR of these values.
+ */
+
+enum sctp_sinfo_flags {
+	MSG_UNORDERED = 1,  /* Send/receive message unordered. */
+	MSG_ADDR_OVER = 2,  /* Override the primary destination. */
+	MSG_ABORT=4,        /* Send an ABORT message to the peer. */
+	/* MSG_EOF is already defined per socket.h */
+};
+
+
+typedef union {
+	__u8   			raw;
+	struct sctp_initmsg	init;
+	struct sctp_sndrcvinfo	sndrcv;
+} sctp_cmsg_data_t;
+
+/* These are cmsg_types.  */
+typedef enum sctp_cmsg_type {
+	SCTP_INIT,              /* 5.2.1 SCTP Initiation Structure */
+	SCTP_SNDRCV,            /* 5.2.2 SCTP Header Information Structure */
+} sctp_cmsg_t;
+
+
+/*
+ * 5.3.1.1 SCTP_ASSOC_CHANGE
+ *
+ *   Communication notifications inform the ULP that an SCTP association
+ *   has either begun or ended. The identifier for a new association is
+ *   provided by this notificaion. The notification information has the
+ *   following format:
+ *
+ */
+
+struct sctp_assoc_change {
+	__u16 sac_type;
+	__u16 sac_flags;
+	__u32 sac_length;
+	__u16 sac_state;
+	__u16 sac_error;
+	__u16 sac_outbound_streams;
+	__u16 sac_inbound_streams;
+	sctp_assoc_t sac_assoc_id;
+};
+
+/*
+ *   sac_state: 32 bits (signed integer)
+ *
+ *   This field holds one of a number of values that communicate the
+ *   event that happened to the association.  They include:
+ *
+ *   Note:  The following state names deviate from the API draft as
+ *   the names clash too easily with other kernel symbols.
+ */
+enum sctp_sac_state {
+	SCTP_COMM_UP,
+	SCTP_COMM_LOST,
+	SCTP_RESTART,
+	SCTP_SHUTDOWN_COMP,
+	SCTP_CANT_STR_ASSOC,
+};
+
+/*
+ * 5.3.1.2 SCTP_PEER_ADDR_CHANGE
+ *
+ *   When a destination address on a multi-homed peer encounters a change
+ *   an interface details event is sent.  The information has the
+ *   following structure:
+ */
+struct sctp_paddr_change {
+	__u16 spc_type;
+	__u16 spc_flags;
+	__u32 spc_length;
+	struct sockaddr_storage spc_aaddr;
+	int spc_state;
+	int spc_error;
+	sctp_assoc_t spc_assoc_id;
+};
+
+/*
+ *    spc_state:  32 bits (signed integer)
+ *
+ *   This field holds one of a number of values that communicate the
+ *   event that happened to the address.  They include:
+ */
+enum sctp_spc_state {
+	ADDRESS_AVAILABLE,
+	ADDRESS_UNREACHABLE,
+	ADDRESS_REMOVED,
+	ADDRESS_ADDED,
+	ADDRESS_MADE_PRIM,
+};
+
+
+/*
+ * 5.3.1.3 SCTP_REMOTE_ERROR
+ *
+ *   A remote peer may send an Operational Error message to its peer.
+ *   This message indicates a variety of error conditions on an
+ *   association. The entire error TLV as it appears on the wire is
+ *   included in a SCTP_REMOTE_ERROR event.  Please refer to the SCTP
+ *   specification [SCTP] and any extensions for a list of possible
+ *   error formats. SCTP error TLVs have the format:
+ */
+struct sctp_remote_error {
+	__u16 sre_type;
+	__u16 sre_flags;
+	__u32 sre_length;
+	__u16 sre_error;
+	__u16 sre_len;
+	sctp_assoc_t sre_assoc_id;
+	__u8 sre_data[0];
+};
+
+
+/*
+ * 5.3.1.4 SCTP_SEND_FAILED
+ *
+ *   If SCTP cannot deliver a message it may return the message as a
+ *   notification.
+ */
+struct sctp_send_failed {
+	__u16 ssf_type;
+	__u16 ssf_flags;
+	__u32 ssf_length;
+	__u32 ssf_error;
+	struct sctp_sndrcvinfo ssf_info;
+	sctp_assoc_t ssf_assoc_id;
+	__u8 ssf_data[0];
+};
+
+/*
+ *   ssf_flags: 16 bits (unsigned integer)
+ *
+ *   The flag value will take one of the following values
+ *
+ *   SCTP_DATA_UNSENT  - Indicates that the data was never put on
+ *                       the wire.
+ *
+ *   SCTP_DATA_SENT    - Indicates that the data was put on the wire.
+ *                       Note that this does not necessarily mean that the
+ *                       data was (or was not) successfully delivered.
+ */
+
+enum sctp_ssf_flags {
+	SCTP_DATA_UNSENT,
+	SCTP_DATA_SENT,
+};
+
+/*
+ * 5.3.1.5 SCTP_SHUTDOWN_EVENT
+ *
+ *   When a peer sends a SHUTDOWN, SCTP delivers this notification to
+ *   inform the application that it should cease sending data.
+ */
+
+struct sctp_shutdown_event {
+	__u16 sse_type;
+	__u16 sse_flags;
+	__u32 sse_length;
+	sctp_assoc_t sse_assoc_id;
+};
+
+/*
+ * 5.3.1.6 SCTP_ADAPTION_INDICATION
+ *
+ *   When a peer sends a Adaption Layer Indication parameter , SCTP
+ *   delivers this notification to inform the application
+ *   that of the peers requested adaption layer.
+ */
+struct sctp_adaption_event {
+	__u16 sai_type;
+	__u16 sai_flags;
+	__u32 sai_length;
+	__u32 sai_adaptation_bits;
+	sctp_assoc_t sse_assoc_id;
+};
+
+/*
+ * 5.3.1.7 SCTP_PARTIAL_DELIVERY_EVENT
+ *
+ *   When a reciever is engaged in a partial delivery of a
+ *   message this notification will be used to inidicate
+ *   various events.
+ */
+
+struct sctp_rcv_pdapi_event {
+	__u16 pdapi_type;
+	__u16 pdapi_flags;
+	__u32 pdapi_length;
+	__u32 pdapi_indication;
+	sctp_assoc_t pdapi_assoc_id;
+};
+
+enum { SCTP_PARTIAL_DELIVERY_ABORTED=0, };
+
+/*
+ * Described in Section 7.3
+ *   Ancillary Data and Notification Interest Options
+ */
+struct sctp_event_subscribe {
+	__u8 sctp_data_io_event;
+	__u8 sctp_association_event;
+	__u8 sctp_address_event;
+	__u8 sctp_send_failure_event;
+	__u8 sctp_peer_error_event;
+	__u8 sctp_shutdown_event;
+	__u8 sctp_partial_delivery_event;
+	__u8 sctp_adaption_layer_event;
+};
+
+/*
+ * 5.3.1 SCTP Notification Structure
+ *
+ *   The notification structure is defined as the union of all
+ *   notification types.
+ *
+ */
+union sctp_notification {
+	struct {
+		__u16 sn_type;             /* Notification type. */
+		__u16 sn_flags;
+		__u32 sn_length;
+	} h;
+	struct sctp_assoc_change sn_assoc_change;
+	struct sctp_paddr_change sn_padr_change;
+	struct sctp_remote_error sn_remote_error;
+	struct sctp_send_failed sn_send_failed;
+	struct sctp_shutdown_event sn_shutdown_event;
+	struct sctp_adaption_event sn_adaption_event;
+	struct sctp_rcv_pdapi_event sn_rcv_pdapi_event;
+};
+
+/* Section 5.3.1
+ * All standard values for sn_type flags are greater than 2^15.
+ * Values from 2^15 and down are reserved.
+ */
+
+enum sctp_sn_type {
+	SCTP_SN_TYPE_BASE     = (1<<15),
+	SCTP_ASSOC_CHANGE,
+	SCTP_PEER_ADDR_CHANGE,
+	SCTP_SEND_FAILED,
+	SCTP_REMOTE_ERROR,
+	SCTP_SHUTDOWN_EVENT,
+	SCTP_PARTIAL_DELIVERY_EVENT,
+	SCTP_ADAPTION_INDICATION,
+};
+
+/* Notification error codes used to fill up the error fields in some
+ * notifications.
+ * SCTP_PEER_ADDRESS_CHAGE 	: spc_error
+ * SCTP_ASSOC_CHANGE		: sac_error
+ * These names should be potentially included in the draft 04 of the SCTP
+ * sockets API specification.
+ */
+typedef enum sctp_sn_error {
+	SCTP_FAILED_THRESHOLD,
+	SCTP_RECEIVED_SACK,
+	SCTP_HEARTBEAT_SUCCESS,
+	SCTP_RESPONSE_TO_USER_REQ,
+	SCTP_INTERNAL_ERROR,
+	SCTP_SHUTDOWN_GUARD_EXPIRES,
+	SCTP_PEER_FAULTY,
+} sctp_sn_error_t;
+
+/*
+ *
+ * 7.1.14 Peer Address Parameters
+ *
+ *   Applications can enable or disable heartbeats for any peer address
+ *   of an association, modify an address's heartbeat interval, force a
+ *   heartbeat to be sent immediately, and adjust the address's maximum
+ *   number of retransmissions sent before an address is considered
+ *   unreachable. The following structure is used to access and modify an
+ *   address's parameters:
+ */
+
+struct sctp_paddrparams {
+	struct sockaddr_storage	spp_address;
+	__u32			spp_hbinterval;
+	__u16			spp_pathmaxrxt;
+	sctp_assoc_t		spp_assoc_id;
+};
+
+/*
+ * 7.2.2 Peer Address Information
+ *
+ *   Applications can retrieve information about a specific peer address
+ *   of an association, including its reachability state, congestion
+ *   window, and retransmission timer values.  This information is
+ *   read-only. The following structure is used to access this
+ *   information:
+ */
+
+struct sctp_paddrinfo {
+	sctp_assoc_t		spinfo_assoc_id;
+	struct sockaddr_storage	spinfo_address;
+	__s32			spinfo_state;
+	__u32			spinfo_cwnd;
+	__u32			spinfo_srtt;
+	__u32			spinfo_rto;
+	__u32			spinfo_mtu;
+};
+
+
+/*
+ * 7.1.1 Retransmission Timeout Parameters (SCTP_RTOINFO)
+ *
+ *   The protocol parameters used to initialize and bound retransmission
+ *   timeout (RTO) are tunable.  See [SCTP] for more information on how
+ *   these parameters are used in RTO calculation.  The peer address
+ *   parameter is ignored for TCP style socket.
+ */
+
+struct sctp_rtoinfo {
+	__u32		srto_initial;
+	__u32		srto_max;
+	__u32		srto_min;
+	sctp_assoc_t	srto_assoc_id;
+};
+
+/*
+ * 7.1.2 Association Retransmission Parameter (SCTP_ASSOCRTXINFO)
+ *
+ *   The protocol parameter used to set the number of retransmissions
+ *   sent before an association is considered unreachable.
+ *   See [SCTP] for more information on how this parameter is used.  The
+ *   peer address parameter is ignored for TCP style socket.
+ */
+
+struct sctp_assocparams {
+	__u16		sasoc_asocmaxrxt;
+	sctp_assoc_t	sasoc_assoc_id;
+};
+
+
+/*
+ * 7.1.9 Set Primary Address (SCTP_SET_PRIMARY_ADDR)
+ *
+ *  Requests that the peer mark the enclosed address as the association
+ *  primary. The enclosed address must be one of the association's
+ *  locally bound addresses. The following structure is used to make a
+ *   set primary request:
+ */
+
+struct sctp_setprim {
+	struct sockaddr_storage ssp_addr;
+	sctp_assoc_t            ssp_assoc_id;
+};
+
+/*
+ * 7.1.10 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR)
+ *
+ *  Requests that the local SCTP stack use the enclosed peer address as
+ *  the association primary. The enclosed address must be one of the
+ *  association peer's addresses. The following structure is used to
+ *  make a set peer primary request:
+ */
+
+struct sctp_setpeerprim {
+	struct sockaddr_storage sspp_addr;
+	sctp_assoc_t            sspp_assoc_id;
+};
+
+/*
+ * 7.2.1 Association Status (SCTP_STATUS)
+ *
+ *   Applications can retrieve current status information about an
+ *   association, including association state, peer receiver window size,
+ *   number of unacked data chunks, and number of data chunks pending
+ *   receipt.  This information is read-only.  The following structure is
+ *   used to access this information:
+ */
+struct sctp_status {
+	sctp_assoc_t		sstat_assoc_id;
+	__s32			sstat_state;
+	__u32			sstat_rwnd;
+	__u16			sstat_unackdata;
+	__u16			sstat_penddata;
+	__u16			sstat_instrms;
+	__u16			sstat_outstrms;
+	__u32			sstat_fragmentation_point;
+	struct sctp_paddrinfo	sstat_primary;
+};
+
+
+/*
+ * 7.1.12 Set Adaption Layer Indicator
+ *
+ * Requests that the local endpoint set the specified Adaption Layer
+ * Indication parameter for all future
+ *  INIT and INIT-ACK exchanges.
+ */
+
+struct sctp_setadaption {
+	__u32	ssb_adaption_ind;
+};
+
+/*
+ * 7.1.12 Set default message time outs (SCTP_SET_STREAM_TIMEOUTS)
+ *
+ * This option requests that the requested stream apply a
+ *  default time-out for messages in queue.
+ */
+struct sctp_setstrm_timeout   {
+	sctp_assoc_t	ssto_assoc_id;
+	__u32		ssto_timeout;
+	__u16		ssto_streamid_start;
+	__u16		ssto_streamid_end;
+};
+
+/*
+ * 8.3 8.5 get all peer/local addresses on a socket
+ * This parameter struct is for getsockopt
+ */
+struct sctp_getaddrs {
+	sctp_assoc_t            assoc_id;
+	int			addr_num;
+	struct sockaddr_storage *addrs;
+};
+
+/* These are bit fields for msghdr->msg_flags.  See section 5.1.  */
+/* On user space Linux, these live in <bits/socket.h> as an enum.  */
+enum sctp_msg_flags {
+	MSG_NOTIFICATION = 0x8000,
+#define MSG_NOTIFICATION MSG_NOTIFICATION
+};
+
+/*
+ * 8.1 sctp_bindx()
+ *
+ * The flags parameter is formed from the bitwise OR of zero or more of the
+ * following currently defined flags:
+ */
+#define BINDX_ADD_ADDR 0x01
+#define BINDX_REM_ADDR 0x02
+
+/* This is the structure that is passed as an argument(optval) to
+ * getsockopt(SCTP_SOCKOPT_PEELOFF).
+ */
+typedef struct {
+	sctp_assoc_t associd;
+	int sd;
+} sctp_peeloff_arg_t;
+
+#endif /* __net_sctp_user_h__ */
+
+
+
diff -urN linux-2.4.23-pre5/include/net/snmp.h linux-2.4.23-pre6/include/net/snmp.h
--- linux-2.4.23-pre5/include/net/snmp.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/net/snmp.h	2003-10-01 10:38:57.000000000 -0700
@@ -212,6 +212,35 @@
 	unsigned long   __pad[0];
 } ____cacheline_aligned; 
 
+/* draft-ietf-sigtran-sctp-mib-07.txt */
+struct sctp_mib
+{
+	unsigned long   SctpCurrEstab;
+	unsigned long   SctpActiveEstabs;
+	unsigned long   SctpPassiveEstabs;
+	unsigned long   SctpAborteds;
+	unsigned long   SctpShutdowns;
+	unsigned long   SctpOutOfBlues;
+	unsigned long   SctpChecksumErrors;
+	unsigned long   SctpOutCtrlChunks;
+	unsigned long   SctpOutOrderChunks;
+	unsigned long   SctpOutUnorderChunks;
+	unsigned long   SctpInCtrlChunks;
+	unsigned long   SctpInOrderChunks;
+	unsigned long   SctpInUnorderChunks;
+	unsigned long   SctpFragUsrMsgs;
+	unsigned long   SctpReasmUsrMsgs;
+	unsigned long   SctpOutSCTPPacks;
+	unsigned long   SctpInSCTPPacks;
+	unsigned long   SctpRtoAlgorithm;
+	unsigned long   SctpRtoMin;
+	unsigned long   SctpRtoMax;
+	unsigned long   SctpRtoInitial;
+	unsigned long   SctpValCookieLife;
+	unsigned long   SctpMaxInitRetr;
+	unsigned long   __pad[0];
+};
+
 struct linux_mib 
 {
 	unsigned long	SyncookiesSent;
diff -urN linux-2.4.23-pre5/include/net/sock.h linux-2.4.23-pre6/include/net/sock.h
--- linux-2.4.23-pre5/include/net/sock.h	2003-06-13 07:51:39.000000000 -0700
+++ linux-2.4.23-pre6/include/net/sock.h	2003-10-01 10:38:57.000000000 -0700
@@ -49,6 +49,9 @@
 #include <linux/icmp.h>
 #endif
 #include <linux/tcp.h>		/* struct tcphdr */
+#if defined(CONFIG_IP_SCTP) || defined (CONFIG_IP_SCTP_MODULE)
+#include <net/sctp/structs.h>	/* struct sctp_opt */
+#endif
 
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>	/* struct sk_buff */
@@ -586,6 +589,9 @@
 
 	union {
 		struct tcp_opt		af_tcp;
+#if defined(CONFIG_IP_SCTP) || defined (CONFIG_IP_SCTP_MODULE)
+		struct sctp_opt		af_sctp;
+#endif
 #if defined(CONFIG_INET) || defined (CONFIG_INET_MODULE)
 		struct raw_opt		tp_raw4;
 #endif
diff -urN linux-2.4.23-pre5/include/net/tcp.h linux-2.4.23-pre6/include/net/tcp.h
--- linux-2.4.23-pre5/include/net/tcp.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/include/net/tcp.h	2003-10-01 10:38:57.000000000 -0700
@@ -555,7 +555,8 @@
  */
 
 struct tcp_func {
-	int			(*queue_xmit)		(struct sk_buff *skb);
+	int			(*queue_xmit)		(struct sk_buff *skb,
+							 int ipfragok);
 
 	void			(*send_check)		(struct sock *sk,
 							 struct tcphdr *th,
diff -urN linux-2.4.23-pre5/kernel/ksyms.c linux-2.4.23-pre6/kernel/ksyms.c
--- linux-2.4.23-pre5/kernel/ksyms.c	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/kernel/ksyms.c	2003-10-01 10:38:57.000000000 -0700
@@ -513,6 +513,9 @@
 EXPORT_SYMBOL(seq_release);
 EXPORT_SYMBOL(seq_read);
 EXPORT_SYMBOL(seq_lseek);
+EXPORT_SYMBOL(single_open);
+EXPORT_SYMBOL(single_release);
+EXPORT_SYMBOL(seq_release_private);
 
 /* Program loader interfaces */
 EXPORT_SYMBOL(setup_arg_pages);
diff -urN linux-2.4.23-pre5/mm/page_alloc.c linux-2.4.23-pre6/mm/page_alloc.c
--- linux-2.4.23-pre5/mm/page_alloc.c	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/mm/page_alloc.c	2003-10-01 10:38:57.000000000 -0700
@@ -317,7 +317,7 @@
 		}
 		current->nr_local_pages = 0;
 	}
- out:
+
 	*freed = __freed;
 	return page;
 }
diff -urN linux-2.4.23-pre5/net/Config.in linux-2.4.23-pre6/net/Config.in
--- linux-2.4.23-pre5/net/Config.in	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/net/Config.in	2003-10-01 10:38:58.000000000 -0700
@@ -29,6 +29,9 @@
    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       source net/khttpd/Config.in
    fi
+   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      source net/sctp/Config.in
+   fi
 fi
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
    tristate 'Asynchronous Transfer Mode (ATM) (EXPERIMENTAL)' CONFIG_ATM
diff -urN linux-2.4.23-pre5/net/Makefile linux-2.4.23-pre6/net/Makefile
--- linux-2.4.23-pre5/net/Makefile	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/net/Makefile	2003-10-01 10:38:59.000000000 -0700
@@ -7,7 +7,7 @@
 
 O_TARGET :=	network.o
 
-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core
+mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core sctp
 export-objs :=	netsyms.o
 
 subdir-y :=	core ethernet
@@ -19,6 +19,7 @@
 subdir-$(CONFIG_NETFILTER)	+= ipv4/netfilter
 subdir-$(CONFIG_UNIX)		+= unix
 subdir-$(CONFIG_IPV6)		+= ipv6
+subdir-$(CONFIG_IP_SCTP)	+= sctp
 
 ifneq ($(CONFIG_IPV6),n)
 ifneq ($(CONFIG_IPV6),)
diff -urN linux-2.4.23-pre5/net/atm/br2684.c linux-2.4.23-pre6/net/atm/br2684.c
--- linux-2.4.23-pre5/net/atm/br2684.c	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/net/atm/br2684.c	2003-10-01 10:38:58.000000000 -0700
@@ -16,9 +16,12 @@
 #include <linux/ip.h>
 #include <asm/uaccess.h>
 #include <net/arp.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
 
 #include <linux/atmbr2684.h>
 
+#include "common.h"
 #include "ipcommon.h"
 
 /*
@@ -768,8 +771,6 @@
 
 extern struct proc_dir_entry *atm_proc_root;	/* from proc.c */
 
-extern int (*br2684_ioctl_hook)(struct atm_vcc *, unsigned int, unsigned long);
-
 /* the following avoids some spurious warnings from the compiler */
 #define UNUSED __attribute__((unused))
 
@@ -779,14 +780,14 @@
 	if ((p = create_proc_entry("br2684", 0, atm_proc_root)) == NULL)
 		return -ENOMEM;
 	p->proc_fops = &br2684_proc_operations;
-	br2684_ioctl_hook = br2684_ioctl;
+	br2684_ioctl_set(br2684_ioctl);
 	return 0;
 }
 
 static void __exit UNUSED br2684_exit(void)
 {
 	struct br2684_dev *brdev;
-	br2684_ioctl_hook = NULL;
+	br2684_ioctl_set(NULL);
 	remove_proc_entry("br2684", atm_proc_root);
 	while (!list_empty(&br2684_devs)) {
 		brdev = list_entry_brdev(br2684_devs.next);
diff -urN linux-2.4.23-pre5/net/atm/clip.c linux-2.4.23-pre6/net/atm/clip.c
--- linux-2.4.23-pre5/net/atm/clip.c	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/net/atm/clip.c	2003-10-01 10:38:58.000000000 -0700
@@ -93,6 +93,7 @@
 		printk(KERN_CRIT "!clip_vcc->entry (clip_vcc %p)\n",clip_vcc);
 		return;
 	}
+	spin_lock_bh(&entry->neigh->dev->xmit_lock);	/* block clip_start_xmit() */
 	entry->neigh->used = jiffies;
 	for (walk = &entry->vccs; *walk; walk = &(*walk)->next)
 		if (*walk == clip_vcc) {
@@ -102,17 +103,20 @@
 			clip_vcc->entry = NULL;
 			if (clip_vcc->xoff)
 				netif_wake_queue(entry->neigh->dev);
-			if (entry->vccs) return;
+			if (entry->vccs)
+				goto out;
 			entry->expires = jiffies-1;
 				/* force resolution or expiration */
 			error = neigh_update(entry->neigh,NULL,NUD_NONE,0,0);
 			if (error)
 				printk(KERN_CRIT "unlink_clip_vcc: "
 				    "neigh_update failed with %d\n",error);
-			return;
+			goto out;
 		}
 	printk(KERN_CRIT "ATMARP: unlink_clip_vcc failed (entry %p, vcc "
 	  "0x%p)\n",entry,clip_vcc);
+out:
+	spin_unlock_bh(&entry->neigh->dev->xmit_lock);
 }
 
 
diff -urN linux-2.4.23-pre5/net/atm/common.c linux-2.4.23-pre6/net/atm/common.c
--- linux-2.4.23-pre5/net/atm/common.c	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/net/atm/common.c	2003-10-01 10:38:58.000000000 -0700
@@ -130,15 +130,35 @@
 #endif
 
 #if defined(CONFIG_PPPOATM) || defined(CONFIG_PPPOATM_MODULE)
-int (*pppoatm_ioctl_hook)(struct atm_vcc *, unsigned int, unsigned long);
-EXPORT_SYMBOL(pppoatm_ioctl_hook);
+static DECLARE_MUTEX(pppoatm_ioctl_mutex);
+
+static int (*pppoatm_ioctl_hook)(struct atm_vcc *, unsigned int, unsigned long);
+
+void pppoatm_ioctl_set(int (*hook)(struct atm_vcc *, unsigned int, unsigned long))
+{
+	down(&pppoatm_ioctl_mutex);
+	pppoatm_ioctl_hook = hook;
+	up(&pppoatm_ioctl_mutex);
+}
+#ifdef CONFIG_PPPOATM_MODULE
+EXPORT_SYMBOL(pppoatm_ioctl_set);
+#endif
 #endif
 
 #if defined(CONFIG_ATM_BR2684) || defined(CONFIG_ATM_BR2684_MODULE)
-int (*br2684_ioctl_hook)(struct atm_vcc *, unsigned int, unsigned long);
-#endif
+static DECLARE_MUTEX(br2684_ioctl_mutex);
+
+static int (*br2684_ioctl_hook)(struct atm_vcc *, unsigned int, unsigned long);
+ 
+void br2684_ioctl_set(int (*hook)(struct atm_vcc *, unsigned int, unsigned long))
+{
+	down(&br2684_ioctl_mutex);
+	br2684_ioctl_hook = hook;
+	up(&br2684_ioctl_mutex);
+}
 #ifdef CONFIG_ATM_BR2684_MODULE
-EXPORT_SYMBOL(br2684_ioctl_hook);
+EXPORT_SYMBOL(br2684_ioctl_set);
+#endif
 #endif
 
 #include "resources.h"		/* atm_find_dev */
@@ -564,129 +584,51 @@
 }
 
 
-static void copy_aal_stats(struct k_atm_aal_stats *from,
-    struct atm_aal_stats *to)
-{
-#define __HANDLE_ITEM(i) to->i = atomic_read(&from->i)
-	__AAL_STAT_ITEMS
-#undef __HANDLE_ITEM
-}
-
-
-static void subtract_aal_stats(struct k_atm_aal_stats *from,
-    struct atm_aal_stats *to)
-{
-#define __HANDLE_ITEM(i) atomic_sub(to->i,&from->i)
-	__AAL_STAT_ITEMS
-#undef __HANDLE_ITEM
-}
-
-
-static int fetch_stats(struct atm_dev *dev,struct atm_dev_stats *arg,int zero)
-{
-	struct atm_dev_stats tmp;
-	int error = 0;
-
-	copy_aal_stats(&dev->stats.aal0,&tmp.aal0);
-	copy_aal_stats(&dev->stats.aal34,&tmp.aal34);
-	copy_aal_stats(&dev->stats.aal5,&tmp.aal5);
-	if (arg) error = copy_to_user(arg,&tmp,sizeof(tmp));
-	if (zero && !error) {
-		subtract_aal_stats(&dev->stats.aal0,&tmp.aal0);
-		subtract_aal_stats(&dev->stats.aal34,&tmp.aal34);
-		subtract_aal_stats(&dev->stats.aal5,&tmp.aal5);
-	}
-	return error ? -EFAULT : 0;
-}
-
-
-int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
+int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
-	struct atm_dev *dev;
-	struct list_head *p;
 	struct atm_vcc *vcc;
-	int *tmp_buf, *tmp_p;
-	void *buf;
-	int error,len,size,number, ret_val;
+	int error;
 
-	ret_val = 0;
 	vcc = ATM_SD(sock);
 	switch (cmd) {
 		case SIOCOUTQ:
 			if (sock->state != SS_CONNECTED ||
-			    !test_bit(ATM_VF_READY,&vcc->flags)) {
-				ret_val =  -EINVAL;
+			    !test_bit(ATM_VF_READY, &vcc->flags)) {
+				error =  -EINVAL;
 				goto done;
 			}
-			ret_val =  put_user(vcc->sk->sndbuf-
-			    atomic_read(&vcc->sk->wmem_alloc),
-			    (int *) arg) ? -EFAULT : 0;
+			error =  put_user(vcc->sk->sndbuf-
+					  atomic_read(&vcc->sk->wmem_alloc),
+					  (int *) arg) ? -EFAULT : 0;
 			goto done;
 		case SIOCINQ:
 			{
 				struct sk_buff *skb;
 
 				if (sock->state != SS_CONNECTED) {
-					ret_val = -EINVAL;
+					error = -EINVAL;
 					goto done;
 				}
 				skb = skb_peek(&vcc->sk->receive_queue);
-				ret_val = put_user(skb ? skb->len : 0,(int *) arg)
-				    ? -EFAULT : 0;
-				goto done;
-			}
-		case ATM_GETNAMES:
-			if (get_user(buf,
-				     &((struct atm_iobuf *) arg)->buffer)) {
-				ret_val = -EFAULT;
-				goto done;
-			}
-			if (get_user(len,
-				     &((struct atm_iobuf *) arg)->length)) {
-				ret_val = -EFAULT;
+				error = put_user(skb ? skb->len : 0,
+						 (int *) arg) ? -EFAULT : 0;
 				goto done;
 			}
-			size = 0;
-			spin_lock(&atm_dev_lock);
-			list_for_each(p, &atm_devs)
-				size += sizeof(int);
-			if (size > len) {
-				spin_unlock(&atm_dev_lock);
-				ret_val = -E2BIG;
-				goto done;
-			}
-			tmp_buf = kmalloc(size, GFP_ATOMIC);
-			if (!tmp_buf) {
-				spin_unlock(&atm_dev_lock);
-				ret_val = -ENOMEM;
-				goto done;
-			}
-			tmp_p = tmp_buf;
-			list_for_each(p, &atm_devs) {
-				dev = list_entry(p, struct atm_dev, dev_list);
-				*tmp_p++ = dev->number;
-			}
-			spin_unlock(&atm_dev_lock);
-		        ret_val = ((copy_to_user(buf, tmp_buf, size)) ||
-			    put_user(size, &((struct atm_iobuf *) arg)->length)
-			    ) ? -EFAULT : 0;
-			kfree(tmp_buf);
-			goto done;
 		case SIOCGSTAMP: /* borrowed from IP */
 			if (!vcc->sk->stamp.tv_sec) {
-				ret_val = -ENOENT;
+				error = -ENOENT;
 				goto done;
 			}
-			ret_val = copy_to_user((void *) arg, &vcc->sk->stamp,
-			    sizeof(struct timeval)) ? -EFAULT : 0;
+			error = copy_to_user((void *) arg, &vcc->sk->stamp,
+					     sizeof(struct timeval)) ? -EFAULT : 0;
 			goto done;
 		case ATM_SETSC:
 			printk(KERN_WARNING "ATM_SETSC is obsolete\n");
-			ret_val = 0;
+			error = 0;
 			goto done;
 		case ATMSIGD_CTRL:
 			if (!capable(CAP_NET_ADMIN)) {
-				ret_val = -EPERM;
+				error = -EPERM;
 				goto done;
 			}
 			/*
@@ -697,29 +639,29 @@
 			 * have the same privledges that /proc/kcore needs
 			 */
 			if (!capable(CAP_SYS_RAWIO)) {
-				ret_val = -EPERM;
+				error = -EPERM;
 				goto done;
 			}
 			error = sigd_attach(vcc);
-			if (!error) sock->state = SS_CONNECTED;
-			ret_val = error;
+			if (!error)
+				sock->state = SS_CONNECTED;
 			goto done;
 #if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
 		case SIOCMKCLIP:
 			if (!capable(CAP_NET_ADMIN)) {
-				ret_val = -EPERM;
+				error = -EPERM;
 				goto done;
 			}
 			if (try_atm_clip_ops()) {
-				ret_val = atm_clip_ops->clip_create(arg);
+				error = atm_clip_ops->clip_create(arg);
 				if (atm_clip_ops->owner)
 					__MOD_DEC_USE_COUNT(atm_clip_ops->owner);
 			} else
-				ret_val = -ENOSYS;
+				error = -ENOSYS;
 			goto done;
 		case ATMARPD_CTRL:
 			if (!capable(CAP_NET_ADMIN)) {
-				ret_val = -EPERM;
+				error = -EPERM;
 				goto done;
 			}
 #if defined(CONFIG_ATM_CLIP_MODULE)
@@ -732,51 +674,50 @@
 					__MOD_DEC_USE_COUNT(atm_clip_ops->owner);
 				if (!error)
 					sock->state = SS_CONNECTED;
-				ret_val = error;
 			} else
-				ret_val = -ENOSYS;
+				error = -ENOSYS;
 			goto done;
 		case ATMARP_MKIP:
 			if (!capable(CAP_NET_ADMIN)) {
-				ret_val = -EPERM;
+				error = -EPERM;
 				goto done;
 			}
 			if (try_atm_clip_ops()) {
-				ret_val = atm_clip_ops->clip_mkip(vcc, arg);
+				error = atm_clip_ops->clip_mkip(vcc, arg);
 				if (atm_clip_ops->owner)
 					__MOD_DEC_USE_COUNT(atm_clip_ops->owner);
 			} else
-				ret_val = -ENOSYS;
+				error = -ENOSYS;
 			goto done;
 		case ATMARP_SETENTRY:
 			if (!capable(CAP_NET_ADMIN)) {
-				ret_val = -EPERM;
+				error = -EPERM;
 				goto done;
 			}
 			if (try_atm_clip_ops()) {
-				ret_val = atm_clip_ops->clip_setentry(vcc, arg);
+				error = atm_clip_ops->clip_setentry(vcc, arg);
 				if (atm_clip_ops->owner)
 					__MOD_DEC_USE_COUNT(atm_clip_ops->owner);
 			} else
-				ret_val = -ENOSYS;
+				error = -ENOSYS;
 			goto done;
 		case ATMARP_ENCAP:
 			if (!capable(CAP_NET_ADMIN)) {
-				ret_val = -EPERM;
+				error = -EPERM;
 				goto done;
 			}
 			if (try_atm_clip_ops()) {
-				ret_val = atm_clip_ops->clip_encap(vcc, arg);
+				error = atm_clip_ops->clip_encap(vcc, arg);
 				if (atm_clip_ops->owner)
 					__MOD_DEC_USE_COUNT(atm_clip_ops->owner);
 			} else
-				ret_val = -ENOSYS;
+				error = -ENOSYS;
 			goto done;
 #endif
 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
                 case ATMLEC_CTRL:
                         if (!capable(CAP_NET_ADMIN)) {
-				ret_val = -EPERM;
+				error = -EPERM;
 				goto done;
 			}
 #if defined(CONFIG_ATM_LANE_MODULE)
@@ -789,39 +730,38 @@
 					__MOD_DEC_USE_COUNT(atm_lane_ops->owner);
 				if (error >= 0)
 					sock->state = SS_CONNECTED;
-				ret_val =  error;
 			} else
-				ret_val = -ENOSYS;
+				error = -ENOSYS;
 			goto done;
                 case ATMLEC_MCAST:
 			if (!capable(CAP_NET_ADMIN)) {
-				ret_val = -EPERM;
+				error = -EPERM;
 				goto done;
 			}
 			if (try_atm_lane_ops()) {
-				ret_val = atm_lane_ops->mcast_attach(vcc, (int) arg);
+				error = atm_lane_ops->mcast_attach(vcc, (int) arg);
 				if (atm_lane_ops->owner)
 					__MOD_DEC_USE_COUNT(atm_lane_ops->owner);
 			} else
-				ret_val = -ENOSYS;
+				error = -ENOSYS;
 			goto done;
                 case ATMLEC_DATA:
 			if (!capable(CAP_NET_ADMIN)) {
-				ret_val = -EPERM;
+				error = -EPERM;
 				goto done;
 			}
 			if (try_atm_lane_ops()) {
-				ret_val = atm_lane_ops->vcc_attach(vcc, (void *) arg);
+				error = atm_lane_ops->vcc_attach(vcc, (void *) arg);
 				if (atm_lane_ops->owner)
 					__MOD_DEC_USE_COUNT(atm_lane_ops->owner);
 			} else
-				ret_val = -ENOSYS;
+				error = -ENOSYS;
 			goto done;
 #endif
 #if defined(CONFIG_ATM_MPOA) || defined(CONFIG_ATM_MPOA_MODULE)
 		case ATMMPC_CTRL:
 			if (!capable(CAP_NET_ADMIN)) {
-				ret_val = -EPERM;
+				error = -EPERM;
 				goto done;
 			}
 #if defined(CONFIG_ATM_MPOA_MODULE)
@@ -834,248 +774,90 @@
 					__MOD_DEC_USE_COUNT(atm_mpoa_ops->owner);
 				if (error >= 0)
 					sock->state = SS_CONNECTED;
-				ret_val = error;
 			} else
-				ret_val = -ENOSYS;
+				error = -ENOSYS;
 			goto done;
 		case ATMMPC_DATA:
 			if (!capable(CAP_NET_ADMIN)) {
-				ret_val = -EPERM;
+				error = -EPERM;
 				goto done;
 			}
 			if (try_atm_mpoa_ops()) {
-				ret_val = atm_mpoa_ops->vcc_attach(vcc, arg);
+				error = atm_mpoa_ops->vcc_attach(vcc, arg);
 				if (atm_mpoa_ops->owner)
 					__MOD_DEC_USE_COUNT(atm_mpoa_ops->owner);
 			} else
-				ret_val = -ENOSYS;
+				error = -ENOSYS;
 			goto done;
 #endif
 #if defined(CONFIG_ATM_TCP) || defined(CONFIG_ATM_TCP_MODULE)
 		case SIOCSIFATMTCP:
 			if (!capable(CAP_NET_ADMIN)) {
-				ret_val = -EPERM;
+				error = -EPERM;
 				goto done;
 			}
 			if (!atm_tcp_ops.attach) {
-				ret_val = -ENOPKG;
+				error = -ENOPKG;
 				goto done;
 			}
-			fops_get (&atm_tcp_ops);
-			error = atm_tcp_ops.attach(vcc,(int) arg);
-			if (error >= 0) sock->state = SS_CONNECTED;
-			else            fops_put (&atm_tcp_ops);
-			ret_val = error;
+			fops_get(&atm_tcp_ops);
+			error = atm_tcp_ops.attach(vcc, (int) arg);
+			if (error >= 0)
+				sock->state = SS_CONNECTED;
+			else
+				fops_put(&atm_tcp_ops);
 			goto done;
 		case ATMTCP_CREATE:
 			if (!capable(CAP_NET_ADMIN)) {
-				ret_val = -EPERM;
+				error = -EPERM;
 				goto done;
 			}
 			if (!atm_tcp_ops.create_persistent) {
-				ret_val = -ENOPKG;
+				error = -ENOPKG;
 				goto done;
 			}
 			error = atm_tcp_ops.create_persistent((int) arg);
-			if (error < 0) fops_put (&atm_tcp_ops);
-			ret_val = error;
+			if (error < 0)
+				fops_put(&atm_tcp_ops);
 			goto done;
 		case ATMTCP_REMOVE:
 			if (!capable(CAP_NET_ADMIN)) {
-				ret_val = -EPERM;
+				error = -EPERM;
 				goto done;
 			}
 			if (!atm_tcp_ops.remove_persistent) {
-				ret_val = -ENOPKG;
+				error = -ENOPKG;
 				goto done;
 			}
 			error = atm_tcp_ops.remove_persistent((int) arg);
-			fops_put (&atm_tcp_ops);
-			ret_val = error;
+			fops_put(&atm_tcp_ops);
 			goto done;
 #endif
 		default:
 			break;
 	}
+	error = -ENOIOCTLCMD;
 #if defined(CONFIG_PPPOATM) || defined(CONFIG_PPPOATM_MODULE)
-	if (pppoatm_ioctl_hook) {
-		ret_val = pppoatm_ioctl_hook(vcc, cmd, arg);
-		if (ret_val != -ENOIOCTLCMD)
-			goto done;
-	}
+	down(&pppoatm_ioctl_mutex);
+	if (pppoatm_ioctl_hook)
+		error = pppoatm_ioctl_hook(vcc, cmd, arg);
+	up(&pppoatm_ioctl_mutex);
+	if (error != -ENOIOCTLCMD)
+		goto done;
 #endif
 #if defined(CONFIG_ATM_BR2684) || defined(CONFIG_ATM_BR2684_MODULE)
-	if (br2684_ioctl_hook) {
-		ret_val = br2684_ioctl_hook(vcc, cmd, arg);
-		if (ret_val != -ENOIOCTLCMD)
-			goto done;
-	}
-#endif
-
-	if (get_user(buf,&((struct atmif_sioc *) arg)->arg)) {
-		ret_val = -EFAULT;
-		goto done;
-	}
-	if (get_user(len,&((struct atmif_sioc *) arg)->length)) {
-		ret_val = -EFAULT;
-		goto done;
-	}
-	if (get_user(number,&((struct atmif_sioc *) arg)->number)) {
-		ret_val = -EFAULT;
-		goto done;
-	}
-	if (!(dev = atm_dev_lookup(number))) {
-		ret_val = -ENODEV;
+	down(&br2684_ioctl_mutex);
+	if (br2684_ioctl_hook)
+		error = br2684_ioctl_hook(vcc, cmd, arg);
+	up(&br2684_ioctl_mutex);
+	if (error != -ENOIOCTLCMD)
 		goto done;
-	}
-	
-	size = 0;
-	switch (cmd) {
-		case ATM_GETTYPE:
-			size = strlen(dev->type)+1;
-			if (copy_to_user(buf,dev->type,size)) {
-				ret_val = -EFAULT;
-				goto done_release;
-			}
-			break;
-		case ATM_GETESI:
-			size = ESI_LEN;
-			if (copy_to_user(buf,dev->esi,size)) {
-				ret_val = -EFAULT;
-				goto done_release;
-			}
-			break;
-		case ATM_SETESI:
-			{
-				int i;
-
-				for (i = 0; i < ESI_LEN; i++)
-					if (dev->esi[i]) {
-						ret_val = -EEXIST;
-						goto done_release;
-					}
-			}
-			/* fall through */
-		case ATM_SETESIF:
-			{
-				unsigned char esi[ESI_LEN];
-
-				if (!capable(CAP_NET_ADMIN)) {
-					ret_val = -EPERM;
-					goto done_release;
-				}
-				if (copy_from_user(esi,buf,ESI_LEN)) {
-					ret_val = -EFAULT;
-					goto done_release;
-				}
-				memcpy(dev->esi,esi,ESI_LEN);
-				ret_val =  ESI_LEN;
-				goto done_release;
-			}
-		case ATM_GETSTATZ:
-			if (!capable(CAP_NET_ADMIN)) {
-				ret_val = -EPERM;
-				goto done_release;
-			}
-			/* fall through */
-		case ATM_GETSTAT:
-			size = sizeof(struct atm_dev_stats);
-			error = fetch_stats(dev,buf,cmd == ATM_GETSTATZ);
-			if (error) {
-				ret_val = error;
-				goto done_release;
-			}
-			break;
-		case ATM_GETCIRANGE:
-			size = sizeof(struct atm_cirange);
-			if (copy_to_user(buf,&dev->ci_range,size)) {
-				ret_val = -EFAULT;
-				goto done_release;
-			}
-			break;
-		case ATM_GETLINKRATE:
-			size = sizeof(int);
-			if (copy_to_user(buf,&dev->link_rate,size)) {
-				ret_val = -EFAULT;
-				goto done_release;
-			}
-			break;
-		case ATM_RSTADDR:
-			if (!capable(CAP_NET_ADMIN)) {
-				ret_val = -EPERM;
-				goto done_release;
-			}
-			atm_reset_addr(dev);
-			break;
-		case ATM_ADDADDR:
-		case ATM_DELADDR:
-			if (!capable(CAP_NET_ADMIN)) {
-				ret_val = -EPERM;
-				goto done_release;
-			}
-			{
-				struct sockaddr_atmsvc addr;
+#endif
 
-				if (copy_from_user(&addr,buf,sizeof(addr))) {
-					ret_val = -EFAULT;
-					goto done_release;
-				}
-				if (cmd == ATM_ADDADDR)
-					ret_val = atm_add_addr(dev,&addr);
-				else
-					ret_val = atm_del_addr(dev,&addr);
-				goto done_release;
-			}
-		case ATM_GETADDR:
-			size = atm_get_addr(dev,buf,len);
-			if (size < 0)
-				ret_val = size;
-			else
-			/* may return 0, but later on size == 0 means "don't
-			   write the length" */
-				ret_val = put_user(size,
-						   &((struct atmif_sioc *) arg)->length) ? -EFAULT : 0;
-			goto done_release;
-		case ATM_SETLOOP:
-			if (__ATM_LM_XTRMT((int) (long) buf) &&
-			    __ATM_LM_XTLOC((int) (long) buf) >
-			    __ATM_LM_XTRMT((int) (long) buf)) {
-				ret_val = -EINVAL;
-				goto done_release;
-			}
-			/* fall through */
-		case ATM_SETCIRANGE:
-		case SONET_GETSTATZ:
-		case SONET_SETDIAG:
-		case SONET_CLRDIAG:
-		case SONET_SETFRAMING:
-			if (!capable(CAP_NET_ADMIN)) {
-				ret_val = -EPERM;
-				goto done_release;
-			}
-			/* fall through */
-		default:
-			if (!dev->ops->ioctl) {
-				ret_val = -EINVAL;
-				goto done_release;
-			}
-			size = dev->ops->ioctl(dev,cmd,buf);
-			if (size < 0) {
-				ret_val = (size == -ENOIOCTLCMD ? -EINVAL : size);
-				goto done_release;
-			}
-	}
-	
-	if (size)
-		ret_val =  put_user(size,&((struct atmif_sioc *) arg)->length) ?
-			-EFAULT : 0;
-	else
-		ret_val = 0;
-done_release:
-	atm_dev_release(dev);
+	error = atm_dev_ioctl(cmd, arg);
 
 done:
-	return ret_val;
+	return error;
 }
 
 
diff -urN linux-2.4.23-pre5/net/atm/common.h linux-2.4.23-pre6/net/atm/common.h
--- linux-2.4.23-pre5/net/atm/common.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/net/atm/common.h	2003-10-01 10:38:58.000000000 -0700
@@ -18,7 +18,7 @@
 int atm_sendmsg(struct socket *sock,struct msghdr *m,int total_len,
   struct scm_cookie *scm);
 unsigned int atm_poll(struct file *file,struct socket *sock,poll_table *wait);
-int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg);
+int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
 int atm_setsockopt(struct socket *sock,int level,int optname,char *optval,
     int optlen);
 int atm_getsockopt(struct socket *sock,int level,int optname,char *optval,
@@ -28,6 +28,9 @@
 void atm_release_vcc_sk(struct sock *sk,int free_sk);
 void atm_shutdown_dev(struct atm_dev *dev);
 
+void pppoatm_ioctl_set(int (*hook)(struct atm_vcc *, unsigned int, unsigned long));
+void br2684_ioctl_set(int (*hook)(struct atm_vcc *, unsigned int, unsigned long));
+
 int atmpvc_init(void);
 void atmpvc_exit(void);
 int atmsvc_init(void);
diff -urN linux-2.4.23-pre5/net/atm/pppoatm.c linux-2.4.23-pre6/net/atm/pppoatm.c
--- linux-2.4.23-pre5/net/atm/pppoatm.c	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/net/atm/pppoatm.c	2003-10-01 10:38:58.000000000 -0700
@@ -44,6 +44,8 @@
 #include <linux/ppp_channel.h>
 #include <linux/atmppp.h>
 
+#include "common.h"
+
 #if 0
 #define DPRINTK(format, args...) \
 	printk(KERN_DEBUG "pppoatm: " format, ##args)
@@ -344,17 +346,15 @@
 /* the following avoids some spurious warnings from the compiler */
 #define UNUSED __attribute__((unused))
 
-extern int (*pppoatm_ioctl_hook)(struct atm_vcc *, unsigned int, unsigned long);
-
 static int __init UNUSED pppoatm_init(void)
 {
-	pppoatm_ioctl_hook = pppoatm_ioctl;
+	pppoatm_ioctl_set(pppoatm_ioctl);
 	return 0;
 }
 
 static void __exit UNUSED pppoatm_exit(void)
 {
-	pppoatm_ioctl_hook = NULL;
+	pppoatm_ioctl_set(NULL);
 }
 
 module_init(pppoatm_init);
diff -urN linux-2.4.23-pre5/net/atm/pvc.c linux-2.4.23-pre6/net/atm/pvc.c
--- linux-2.4.23-pre5/net/atm/pvc.c	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/net/atm/pvc.c	2003-10-01 10:38:58.000000000 -0700
@@ -74,24 +74,24 @@
 
 
 static struct proto_ops SOCKOPS_WRAPPED(pvc_proto_ops) = {
-	family:		PF_ATMPVC,
+	.family =	PF_ATMPVC,
 
-	release:	atm_release,
-	bind:		pvc_bind,
-	connect:	pvc_connect,
-	socketpair:	sock_no_socketpair,
-	accept:		sock_no_accept,
-	getname:	pvc_getname,
-	poll:		atm_poll,
-	ioctl:		atm_ioctl,
-	listen:		sock_no_listen,
-	shutdown:	pvc_shutdown,
-	setsockopt:	atm_setsockopt,
-	getsockopt:	atm_getsockopt,
-	sendmsg:	atm_sendmsg,
-	recvmsg:	atm_recvmsg,
-	mmap:		sock_no_mmap,
-	sendpage:	sock_no_sendpage,
+	.release =	atm_release,
+	.bind =		pvc_bind,
+	.connect =	pvc_connect,
+	.socketpair =	sock_no_socketpair,
+	.accept =	sock_no_accept,
+	.getname =	pvc_getname,
+	.poll =		atm_poll,
+	.ioctl =	vcc_ioctl,
+	.listen =	sock_no_listen,
+	.shutdown =	pvc_shutdown,
+	.setsockopt =	atm_setsockopt,
+	.getsockopt =	atm_getsockopt,
+	.sendmsg =	atm_sendmsg,
+	.recvmsg =	atm_recvmsg,
+	.mmap =		sock_no_mmap,
+	.sendpage =	sock_no_sendpage,
 };
 
 
diff -urN linux-2.4.23-pre5/net/atm/resources.c linux-2.4.23-pre6/net/atm/resources.c
--- linux-2.4.23-pre5/net/atm/resources.c	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/net/atm/resources.c	2003-10-01 10:38:58.000000000 -0700
@@ -7,6 +7,7 @@
 #include <linux/ctype.h>
 #include <linux/string.h>
 #include <linux/atmdev.h>
+#include <linux/sonet.h>
 #include <linux/kernel.h> /* for barrier */
 #include <linux/module.h>
 #include <linux/bitops.h>
@@ -15,6 +16,7 @@
 
 #include "common.h"
 #include "resources.h"
+#include "addr.h"
 
 
 #ifndef NULL
@@ -172,6 +174,238 @@
 }
 
 
+static void copy_aal_stats(struct k_atm_aal_stats *from,
+    struct atm_aal_stats *to)
+{
+#define __HANDLE_ITEM(i) to->i = atomic_read(&from->i)
+	__AAL_STAT_ITEMS
+#undef __HANDLE_ITEM
+}
+
+
+static void subtract_aal_stats(struct k_atm_aal_stats *from,
+    struct atm_aal_stats *to)
+{
+#define __HANDLE_ITEM(i) atomic_sub(to->i, &from->i)
+	__AAL_STAT_ITEMS
+#undef __HANDLE_ITEM
+}
+
+
+static int fetch_stats(struct atm_dev *dev, struct atm_dev_stats *arg, int zero)
+{
+	struct atm_dev_stats tmp;
+	int error = 0;
+
+	copy_aal_stats(&dev->stats.aal0, &tmp.aal0);
+	copy_aal_stats(&dev->stats.aal34, &tmp.aal34);
+	copy_aal_stats(&dev->stats.aal5, &tmp.aal5);
+	if (arg)
+		error = copy_to_user(arg, &tmp, sizeof(tmp));
+	if (zero && !error) {
+		subtract_aal_stats(&dev->stats.aal0, &tmp.aal0);
+		subtract_aal_stats(&dev->stats.aal34, &tmp.aal34);
+		subtract_aal_stats(&dev->stats.aal5, &tmp.aal5);
+	}
+	return error ? -EFAULT : 0;
+}
+
+
+int atm_dev_ioctl(unsigned int cmd, unsigned long arg)
+{
+	void *buf;
+	int error = 0, len, number, size = 0;
+	struct atm_dev *dev;
+
+	if (cmd == ATM_GETNAMES) {
+		int *tmp_buf, *tmp_bufp;
+		struct list_head *p;
+		/*
+		 * ATM_GETNAMES is a special case: it doesn't require a
+		 * device number argument
+		 */
+		if (get_user(buf, &((struct atm_iobuf *) arg)->buffer))
+			return -EFAULT;
+		if (get_user(len, &((struct atm_iobuf *) arg)->length))
+			return -EFAULT;
+		spin_lock(&atm_dev_lock);
+		list_for_each(p, &atm_devs)
+			size += sizeof(int);
+		if (size > len) {
+			spin_unlock(&atm_dev_lock);
+			return -E2BIG;
+		}
+		tmp_buf = tmp_bufp = kmalloc(size, GFP_ATOMIC);
+		if (!tmp_buf) {
+			spin_unlock(&atm_dev_lock);
+			return -ENOMEM;
+		}
+		list_for_each(p, &atm_devs) {
+			dev = list_entry(p, struct atm_dev, dev_list);
+			*tmp_bufp++ = dev->number;
+		}
+		spin_unlock(&atm_dev_lock);
+	        error = (copy_to_user(buf, tmp_buf, size) ||
+				put_user(size, &((struct atm_iobuf *) arg)->length))
+					? -EFAULT : 0;
+		kfree(tmp_buf);
+		return error;
+	}
+
+	if (get_user(buf, &((struct atmif_sioc *) arg)->arg))
+		return -EFAULT;
+	if (get_user(len, &((struct atmif_sioc *) arg)->length))
+		return -EFAULT;
+	if (get_user(number, &((struct atmif_sioc *) arg)->number))
+		return -EFAULT;
+
+	if (!(dev = atm_dev_lookup(number)))
+		return -ENODEV;
+	
+	switch (cmd) {
+		case ATM_GETTYPE:
+			size = strlen(dev->type) + 1;
+			if (copy_to_user(buf, dev->type, size)) {
+				error = -EFAULT;
+				goto done;
+			}
+			break;
+		case ATM_GETESI:
+			size = ESI_LEN;
+			if (copy_to_user(buf, dev->esi, size)) {
+				error = -EFAULT;
+				goto done;
+			}
+			break;
+		case ATM_SETESI:
+			{
+				int i;
+
+				for (i = 0; i < ESI_LEN; i++)
+					if (dev->esi[i]) {
+						error = -EEXIST;
+						goto done;
+					}
+			}
+			/* fall through */
+		case ATM_SETESIF:
+			{
+				unsigned char esi[ESI_LEN];
+
+				if (!capable(CAP_NET_ADMIN)) {
+					error = -EPERM;
+					goto done;
+				}
+				if (copy_from_user(esi, buf, ESI_LEN)) {
+					error = -EFAULT;
+					goto done;
+				}
+				memcpy(dev->esi, esi, ESI_LEN);
+				error =  ESI_LEN;
+				goto done;
+			}
+		case ATM_GETSTATZ:
+			if (!capable(CAP_NET_ADMIN)) {
+				error = -EPERM;
+				goto done;
+			}
+			/* fall through */
+		case ATM_GETSTAT:
+			size = sizeof(struct atm_dev_stats);
+			error = fetch_stats(dev, buf, cmd == ATM_GETSTATZ);
+			if (error)
+				goto done;
+			break;
+		case ATM_GETCIRANGE:
+			size = sizeof(struct atm_cirange);
+			if (copy_to_user(buf, &dev->ci_range, size)) {
+				error = -EFAULT;
+				goto done;
+			}
+			break;
+		case ATM_GETLINKRATE:
+			size = sizeof(int);
+			if (copy_to_user(buf, &dev->link_rate, size)) {
+				error = -EFAULT;
+				goto done;
+			}
+			break;
+		case ATM_RSTADDR:
+			if (!capable(CAP_NET_ADMIN)) {
+				error = -EPERM;
+				goto done;
+			}
+			atm_reset_addr(dev);
+			break;
+		case ATM_ADDADDR:
+		case ATM_DELADDR:
+			if (!capable(CAP_NET_ADMIN)) {
+				error = -EPERM;
+				goto done;
+			}
+			{
+				struct sockaddr_atmsvc addr;
+
+				if (copy_from_user(&addr, buf, sizeof(addr))) {
+					error = -EFAULT;
+					goto done;
+				}
+				if (cmd == ATM_ADDADDR)
+					error = atm_add_addr(dev, &addr);
+				else
+					error = atm_del_addr(dev, &addr);
+				goto done;
+			}
+		case ATM_GETADDR:
+			error = atm_get_addr(dev, buf, len);
+			if (error < 0)
+				goto done;
+			size = error;
+			/* write back size even if it's zero */
+			goto write_size;
+		case ATM_SETLOOP:
+			if (__ATM_LM_XTRMT((int) (long) buf) &&
+			    __ATM_LM_XTLOC((int) (long) buf) >
+			    __ATM_LM_XTRMT((int) (long) buf)) {
+				error = -EINVAL;
+				goto done;
+			}
+			/* fall through */
+		case ATM_SETCIRANGE:
+		case SONET_GETSTATZ:
+		case SONET_SETDIAG:
+		case SONET_CLRDIAG:
+		case SONET_SETFRAMING:
+			if (!capable(CAP_NET_ADMIN)) {
+				error = -EPERM;
+				goto done;
+			}
+			/* fall through */
+		default:
+			if (!dev->ops->ioctl) {
+				error = -EINVAL;
+				goto done;
+			}
+			size = dev->ops->ioctl(dev, cmd, buf);
+			if (size < 0) {
+				error = (size == -ENOIOCTLCMD ? -EINVAL : size);
+				goto done;
+			}
+	}
+	
+	if (size) {
+write_size:
+		error = put_user(size,
+			  &((struct atmif_sioc *) arg)->length)
+			  ? -EFAULT : 0;
+	}
+done:
+	atm_dev_release(dev);
+	return error;
+}
+
+
+
 /* Handler for sk->destruct, invoked by sk_free() */
 static void atm_free_sock(struct sock *sk)
 {
diff -urN linux-2.4.23-pre5/net/atm/resources.h linux-2.4.23-pre6/net/atm/resources.h
--- linux-2.4.23-pre5/net/atm/resources.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/net/atm/resources.h	2003-10-01 10:38:58.000000000 -0700
@@ -16,6 +16,7 @@
 
 struct sock *alloc_atm_vcc_sk(int family);
 void free_atm_vcc_sk(struct sock *sk);
+int atm_dev_ioctl(unsigned int cmd, unsigned long arg);
 
 
 #ifdef CONFIG_PROC_FS
diff -urN linux-2.4.23-pre5/net/atm/svc.c linux-2.4.23-pre6/net/atm/svc.c
--- linux-2.4.23-pre5/net/atm/svc.c	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/net/atm/svc.c	2003-10-01 10:38:58.000000000 -0700
@@ -392,24 +392,24 @@
 
 
 static struct proto_ops SOCKOPS_WRAPPED(svc_proto_ops) = {
-	family:		PF_ATMSVC,
+	.family =	PF_ATMSVC,
 
-	release:	svc_release,
-	bind:		svc_bind,
-	connect:	svc_connect,
-	socketpair:	sock_no_socketpair,
-	accept:		svc_accept,
-	getname:	svc_getname,
-	poll:		atm_poll,
-	ioctl:		atm_ioctl,
-	listen:		svc_listen,
-	shutdown:	svc_shutdown,
-	setsockopt:	svc_setsockopt,
-	getsockopt:	svc_getsockopt,
-	sendmsg:	atm_sendmsg,
-	recvmsg:	atm_recvmsg,
-	mmap:		sock_no_mmap,
-	sendpage:	sock_no_sendpage,
+	.release =	svc_release,
+	.bind =		svc_bind,
+	.connect =	svc_connect,
+	.socketpair =	sock_no_socketpair,
+	.accept =	svc_accept,
+	.getname =	svc_getname,
+	.poll =		atm_poll,
+	.ioctl =	vcc_ioctl,
+	.listen =	svc_listen,
+	.shutdown =	svc_shutdown,
+	.setsockopt =	svc_setsockopt,
+	.getsockopt =	svc_getsockopt,
+	.sendmsg =	atm_sendmsg,
+	.recvmsg =	atm_recvmsg,
+	.mmap =		sock_no_mmap,
+	.sendpage =	sock_no_sendpage,
 };
 
 
diff -urN linux-2.4.23-pre5/net/core/dev.c linux-2.4.23-pre6/net/core/dev.c
--- linux-2.4.23-pre5/net/core/dev.c	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/net/core/dev.c	2003-10-01 10:38:58.000000000 -0700
@@ -1570,8 +1570,8 @@
 
 #ifdef CONFIG_NET_HW_FLOWCONTROL
 		if (queue->throttle && queue->input_pkt_queue.qlen < no_cong_thresh ) {
+			queue->throttle = 0;
 			if (atomic_dec_and_test(&netdev_dropping)) {
-				queue->throttle = 0;
 				netdev_wakeup();
 				break;
 			}
diff -urN linux-2.4.23-pre5/net/ipv4/af_inet.c linux-2.4.23-pre6/net/ipv4/af_inet.c
--- linux-2.4.23-pre5/net/ipv4/af_inet.c	2003-06-13 07:51:39.000000000 -0700
+++ linux-2.4.23-pre6/net/ipv4/af_inet.c	2003-10-01 10:38:59.000000000 -0700
@@ -470,7 +470,7 @@
 /* It is off by default, see below. */
 int sysctl_ip_nonlocal_bind;
 
-static int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
 	struct sockaddr_in *addr=(struct sockaddr_in *)uaddr;
 	struct sock *sk=sock->sk;
@@ -703,7 +703,7 @@
  *	This does both peername and sockname.
  */
  
-static int inet_getname(struct socket *sock, struct sockaddr *uaddr,
+int inet_getname(struct socket *sock, struct sockaddr *uaddr,
 		 int *uaddr_len, int peer)
 {
 	struct sock *sk		= sock->sk;
@@ -820,7 +820,7 @@
  *	There's a good 20K of config code hanging around the kernel.
  */
 
-static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
 	struct sock *sk = sock->sk;
 	int err;
diff -urN linux-2.4.23-pre5/net/ipv4/arp.c linux-2.4.23-pre6/net/ipv4/arp.c
--- linux-2.4.23-pre5/net/ipv4/arp.c	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/net/ipv4/arp.c	2003-10-01 10:38:59.000000000 -0700
@@ -852,8 +852,15 @@
 
 int arp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
 {
-	struct arphdr *arp = skb->nh.arph;
+	struct arphdr *arp;
 
+	/* ARP header, plus 2 device addresses, plus 2 IP addresses.  */
+	if (!pskb_may_pull(skb, (sizeof(struct arphdr) +
+				 (2 * dev->addr_len) +
+				 (2 * sizeof(u32)))))
+		goto freeskb;
+
+	arp = skb->nh.arph;
 	if (arp->ar_hln != dev->addr_len ||
 	    dev->flags & IFF_NOARP ||
 	    skb->pkt_type == PACKET_OTHERHOST ||
@@ -864,11 +871,6 @@
 	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
 		goto out_of_mem;
 
-	if (skb_is_nonlinear(skb)) {
-		if (skb_linearize(skb, GFP_ATOMIC) != 0)
-			goto freeskb;
-	}
-
 	return NF_HOOK(NF_ARP, NF_ARP_IN, skb, dev, NULL, arp_process);
 
 freeskb:
diff -urN linux-2.4.23-pre5/net/ipv4/igmp.c linux-2.4.23-pre6/net/ipv4/igmp.c
--- linux-2.4.23-pre5/net/ipv4/igmp.c	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/net/ipv4/igmp.c	2003-10-01 10:38:59.000000000 -0700
@@ -285,8 +285,10 @@
 		return 0;
 	}
 	skb = alloc_skb(size + dev->hard_header_len + 15, GFP_ATOMIC);
-	if (skb == NULL)
+	if (skb == NULL) {
+		ip_rt_put(rt);
 		return 0;
+	}
 
 	skb->dst = &rt->u.dst;
 	skb->dev = dev;
diff -urN linux-2.4.23-pre5/net/ipv4/ip_output.c linux-2.4.23-pre6/net/ipv4/ip_output.c
--- linux-2.4.23-pre5/net/ipv4/ip_output.c	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/net/ipv4/ip_output.c	2003-10-01 10:38:59.000000000 -0700
@@ -339,7 +339,7 @@
 	return ip_fragment(skb, skb->dst->output);
 }
 
-int ip_queue_xmit(struct sk_buff *skb)
+int ip_queue_xmit(struct sk_buff *skb, int ipfragok)
 {
 	struct sock *sk = skb->sk;
 	struct ip_options *opt = sk->protinfo.af_inet.opt;
@@ -384,7 +384,7 @@
 	iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));
 	*((__u16 *)iph)	= htons((4 << 12) | (5 << 8) | (sk->protinfo.af_inet.tos & 0xff));
 	iph->tot_len = htons(skb->len);
-	if (ip_dont_fragment(sk, &rt->u.dst))
+	if (ip_dont_fragment(sk, &rt->u.dst) && !ipfragok)
 		iph->frag_off = htons(IP_DF);
 	else
 		iph->frag_off = 0;
diff -urN linux-2.4.23-pre5/net/ipv4/netfilter/ip_nat_snmp_basic.c linux-2.4.23-pre6/net/ipv4/netfilter/ip_nat_snmp_basic.c
--- linux-2.4.23-pre5/net/ipv4/netfilter/ip_nat_snmp_basic.c	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/net/ipv4/netfilter/ip_nat_snmp_basic.c	2003-10-01 10:38:59.000000000 -0700
@@ -53,9 +53,9 @@
 #include <linux/brlock.h>
 #include <linux/types.h>
 #include <linux/ip.h>
+#include <net/checksum.h>
 #include <net/udp.h>
 #include <asm/uaccess.h>
-#include <asm/checksum.h>
 
 
 
diff -urN linux-2.4.23-pre5/net/ipv4/netfilter/ipt_REJECT.c linux-2.4.23-pre6/net/ipv4/netfilter/ipt_REJECT.c
--- linux-2.4.23-pre5/net/ipv4/netfilter/ipt_REJECT.c	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/net/ipv4/netfilter/ipt_REJECT.c	2003-10-01 10:38:59.000000000 -0700
@@ -34,16 +34,17 @@
 		attach(new_skb, nfct);
 }
 
-static inline struct rtable *route_reverse(struct sk_buff *skb, int local)
+static inline struct rtable *route_reverse(struct sk_buff *skb, int hook)
 {
 	struct iphdr *iph = skb->nh.iph;
 	struct dst_entry *odst;
 	struct rt_key key = {};
 	struct rtable *rt;
 
-	if (local) {
+	if (hook != NF_IP_FORWARD) {
 		key.dst = iph->saddr;
-		key.src = iph->daddr;
+		if (hook == NF_IP_LOCAL_IN)
+			key.src = iph->daddr;
 		key.tos = RT_TOS(iph->tos);
 
 		if (ip_route_output_key(&rt, &key) != 0)
@@ -75,7 +76,7 @@
 }
 
 /* Send RST reply */
-static void send_reset(struct sk_buff *oldskb, int local)
+static void send_reset(struct sk_buff *oldskb, int hook)
 {
 	struct sk_buff *nskb;
 	struct tcphdr *otcph, *tcph;
@@ -104,7 +105,7 @@
 			 csum_partial((char *)otcph, otcplen, 0)) != 0)
 		return;
 
-	if ((rt = route_reverse(oldskb, local)) == NULL)
+	if ((rt = route_reverse(oldskb, hook)) == NULL)
 		return;
 
 	hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
@@ -372,7 +373,7 @@
 		send_unreach(*pskb, ICMP_PKT_FILTERED);
 		break;
 	case IPT_TCP_RESET:
-		send_reset(*pskb, hooknum == NF_IP_LOCAL_IN);
+		send_reset(*pskb, hooknum);
 	case IPT_ICMP_ECHOREPLY:
 		/* Doesn't happen. */
 		break;
diff -urN linux-2.4.23-pre5/net/ipv4/tcp_output.c linux-2.4.23-pre6/net/ipv4/tcp_output.c
--- linux-2.4.23-pre5/net/ipv4/tcp_output.c	2003-06-13 07:51:39.000000000 -0700
+++ linux-2.4.23-pre6/net/ipv4/tcp_output.c	2003-10-01 10:38:59.000000000 -0700
@@ -275,7 +275,7 @@
 
 		TCP_INC_STATS(TcpOutSegs);
 
-		err = tp->af_specific->queue_xmit(skb);
+		err = tp->af_specific->queue_xmit(skb, 0);
 		if (err <= 0)
 			return err;
 
diff -urN linux-2.4.23-pre5/net/ipv6/Makefile linux-2.4.23-pre6/net/ipv6/Makefile
--- linux-2.4.23-pre5/net/ipv6/Makefile	2003-06-13 07:51:39.000000000 -0700
+++ linux-2.4.23-pre6/net/ipv6/Makefile	2003-10-01 10:38:59.000000000 -0700
@@ -13,7 +13,9 @@
 		route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.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
+		ip6_flowlabel.o ipv6_syms.o
+
+export-objs := ipv6_syms.o
 
 obj-m  := $(O_TARGET)
 
diff -urN linux-2.4.23-pre5/net/ipv6/addrconf.c linux-2.4.23-pre6/net/ipv6/addrconf.c
--- linux-2.4.23-pre5/net/ipv6/addrconf.c	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/net/ipv6/addrconf.c	2003-10-01 10:38:59.000000000 -0700
@@ -1981,11 +1981,112 @@
 	netlink_broadcast(rtnl, skb, 0, RTMGRP_IPV6_IFADDR, GFP_ATOMIC);
 }
 
+static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
+				__s32 *array, int bytes)
+{
+	memset(array, 0, bytes);
+	array[DEVCONF_FORWARDING] = cnf->forwarding;
+	array[DEVCONF_HOPLIMIT] = cnf->hop_limit;
+	array[DEVCONF_MTU6] = cnf->mtu6;
+	array[DEVCONF_ACCEPT_RA] = cnf->accept_ra;
+	array[DEVCONF_ACCEPT_REDIRECTS] = cnf->accept_redirects;
+	array[DEVCONF_AUTOCONF] = cnf->autoconf;
+	array[DEVCONF_DAD_TRANSMITS] = cnf->dad_transmits;
+	array[DEVCONF_RTR_SOLICITS] = cnf->rtr_solicits;
+	array[DEVCONF_RTR_SOLICIT_INTERVAL] = cnf->rtr_solicit_interval;
+	array[DEVCONF_RTR_SOLICIT_DELAY] = cnf->rtr_solicit_delay;
+#ifdef CONFIG_IPV6_PRIVACY
+	array[DEVCONF_USE_TEMPADDR] = cnf->use_tempaddr;
+	array[DEVCONF_TEMP_VALID_LFT] = cnf->temp_valid_lft;
+	array[DEVCONF_TEMP_PREFERED_LFT] = cnf->temp_prefered_lft;
+	array[DEVCONF_REGEN_MAX_RETRY] = cnf->regen_max_retry;
+	array[DEVCONF_MAX_DESYNC_FACTOR] = cnf->max_desync_factor;
+#endif
+}
+
+static int inet6_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
+			    struct inet6_dev *idev,
+			    int type, u32 pid, u32 seq)
+{
+	__s32			*array = NULL;
+	struct ifinfomsg	*r;
+	struct nlmsghdr 	*nlh;
+	unsigned char		*b = skb->tail;
+	struct rtattr		*subattr;
+
+	nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*r));
+	if (pid) nlh->nlmsg_flags |= NLM_F_MULTI;
+	r = NLMSG_DATA(nlh);
+	r->ifi_family = AF_INET6;
+	r->ifi_type = dev->type;
+	r->ifi_index = dev->ifindex;
+	r->ifi_flags = dev->flags;
+	r->ifi_change = 0;
+	if (!netif_running(dev) || !netif_carrier_ok(dev))
+		r->ifi_flags &= ~IFF_RUNNING;
+	else
+		r->ifi_flags |= IFF_RUNNING;
+
+	RTA_PUT(skb, IFLA_IFNAME, strlen(dev->name)+1, dev->name);
+
+	subattr = (struct rtattr*)skb->tail;
+
+	RTA_PUT(skb, IFLA_PROTINFO, 0, NULL);
+
+	/* return the device flags */
+	RTA_PUT(skb, IFLA_INET6_FLAGS, sizeof(__u32), &idev->if_flags);
+
+	/* return the device sysctl params */
+	if ((array = kmalloc(DEVCONF_MAX * sizeof(*array), GFP_ATOMIC)) == NULL)
+		goto rtattr_failure;
+	ipv6_store_devconf(&idev->cnf, array, DEVCONF_MAX * sizeof(*array));
+	RTA_PUT(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(*array), array);
+
+	/* XXX - Statistics/MC not implemented */
+	subattr->rta_len = skb->tail - (u8*)subattr;
+
+	nlh->nlmsg_len = skb->tail - b;
+	kfree(array);
+	return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+	if (array)
+		kfree(array);
+	skb_trim(skb, b - skb->data);
+	return -1;
+}
+
+static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	int idx, err;
+	int s_idx = cb->args[0];
+	struct net_device *dev;
+	struct inet6_dev *idev;
+
+	read_lock(&dev_base_lock);
+	for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
+		if (idx < s_idx)
+			continue;
+		if ((idev = in6_dev_get(dev)) == NULL)
+			continue;
+		err = inet6_fill_ifinfo(skb, dev, idev, RTM_NEWLINK,
+				NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq);
+		in6_dev_put(idev);
+		if (err <= 0)
+			break;
+	}
+	read_unlock(&dev_base_lock);
+	cb->args[0] = idx;
+
+	return skb->len;
+}
+
 static struct rtnetlink_link inet6_rtnetlink_table[RTM_MAX-RTM_BASE+1] =
 {
 	{ NULL,			NULL,			},
 	{ NULL,			NULL,			},
-	{ NULL,			NULL,			},
+	{ NULL,			inet6_dump_ifinfo,	},
 	{ NULL,			NULL,			},
 
 	{ inet6_rtm_newaddr,	NULL,			},
diff -urN linux-2.4.23-pre5/net/ipv6/af_inet6.c linux-2.4.23-pre6/net/ipv6/af_inet6.c
--- linux-2.4.23-pre5/net/ipv6/af_inet6.c	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/net/ipv6/af_inet6.c	2003-10-01 10:38:59.000000000 -0700
@@ -233,7 +233,7 @@
 
 
 /* bind for INET6 API */
-static int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
 	struct sockaddr_in6 *addr=(struct sockaddr_in6 *)uaddr;
 	struct sock *sk = sock->sk;
@@ -329,7 +329,7 @@
 	return 0;
 }
 
-static int inet6_release(struct socket *sock)
+int inet6_release(struct socket *sock)
 {
 	struct sock *sk = sock->sk;
 
@@ -376,7 +376,7 @@
  *	This does both peername and sockname.
  */
  
-static int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
+int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
 		 int *uaddr_len, int peer)
 {
 	struct sockaddr_in6 *sin=(struct sockaddr_in6 *)uaddr;
@@ -413,7 +413,7 @@
 	return(0);
 }
 
-static int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
 	struct sock *sk = sock->sk;
 	int err = -EINVAL;
diff -urN linux-2.4.23-pre5/net/ipv6/ipv6_syms.c linux-2.4.23-pre6/net/ipv6/ipv6_syms.c
--- linux-2.4.23-pre5/net/ipv6/ipv6_syms.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/ipv6/ipv6_syms.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,35 @@
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <net/protocol.h>
+#include <net/ipv6.h>
+#include <net/addrconf.h>
+#include <net/ip6_route.h>
+
+EXPORT_SYMBOL(ipv6_addr_type);
+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);
+#ifdef CONFIG_NETFILTER
+EXPORT_SYMBOL(ip6_route_me_harder);
+#endif
+EXPORT_SYMBOL(addrconf_lock);
+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);
diff -urN linux-2.4.23-pre5/net/ipv6/ndisc.c linux-2.4.23-pre6/net/ipv6/ndisc.c
--- linux-2.4.23-pre5/net/ipv6/ndisc.c	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/net/ipv6/ndisc.c	2003-10-01 10:38:59.000000000 -0700
@@ -944,6 +944,17 @@
 		in6_dev->if_flags |= IF_RA_RCVD;
 	}
 
+	/*
+	 * Remember the managed/otherconf flags from most recently
+	 * received RA message (RFC 2462) -- yoshfuji
+	 */
+	in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
+				IF_RA_OTHERCONF)) |
+				(ra_msg->icmph.icmp6_addrconf_managed ?
+					IF_RA_MANAGED : 0) |
+				(ra_msg->icmph.icmp6_addrconf_other ?
+					IF_RA_OTHERCONF : 0);
+
 	lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
 
 	rt = rt6_get_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
diff -urN linux-2.4.23-pre5/net/ipv6/tcp_ipv6.c linux-2.4.23-pre6/net/ipv6/tcp_ipv6.c
--- linux-2.4.23-pre5/net/ipv6/tcp_ipv6.c	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/net/ipv6/tcp_ipv6.c	2003-10-01 10:38:59.000000000 -0700
@@ -59,7 +59,7 @@
 				  struct sk_buff *skb);
 
 static int	tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb);
-static int	tcp_v6_xmit(struct sk_buff *skb);
+static int	tcp_v6_xmit(struct sk_buff *skb, int ipfragok);
 
 static struct tcp_func ipv6_mapped;
 static struct tcp_func ipv6_specific;
@@ -1715,7 +1715,7 @@
 	return 0;
 }
 
-static int tcp_v6_xmit(struct sk_buff *skb)
+static int tcp_v6_xmit(struct sk_buff *skb, int ipfragok)
 {
 	struct sock *sk = skb->sk;
 	struct ipv6_pinfo * np = &sk->net_pinfo.af_inet6;
diff -urN linux-2.4.23-pre5/net/netsyms.c linux-2.4.23-pre6/net/netsyms.c
--- linux-2.4.23-pre5/net/netsyms.c	2003-10-01 10:38:43.000000000 -0700
+++ linux-2.4.23-pre6/net/netsyms.c	2003-10-01 10:38:59.000000000 -0700
@@ -60,7 +60,7 @@
 
 extern struct net_proto_family inet_family_ops;
 
-#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE)
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE) || defined (CONFIG_IP_SCTP_MODULE)
 #include <linux/in6.h>
 #include <linux/icmpv6.h>
 #include <net/ipv6.h>
@@ -164,6 +164,7 @@
 EXPORT_SYMBOL(put_cmsg);
 EXPORT_SYMBOL(sock_kmalloc);
 EXPORT_SYMBOL(sock_kfree_s);
+EXPORT_SYMBOL(sock_map_fd);
 EXPORT_SYMBOL(sockfd_lookup);
 
 #ifdef CONFIG_FILTER
@@ -256,6 +257,8 @@
 EXPORT_SYMBOL(ip_route_output_key);
 EXPORT_SYMBOL(ip_route_input);
 EXPORT_SYMBOL(icmp_send);
+EXPORT_SYMBOL(icmp_statistics);
+EXPORT_SYMBOL(icmp_err_convert);
 EXPORT_SYMBOL(ip_options_compile);
 EXPORT_SYMBOL(ip_options_undo);
 EXPORT_SYMBOL(arp_send);
@@ -294,19 +297,7 @@
 #endif
 
 
-#ifdef CONFIG_IPV6
-EXPORT_SYMBOL(ipv6_addr_type);
-EXPORT_SYMBOL(icmpv6_send);
-EXPORT_SYMBOL(ndisc_mc_map);
-EXPORT_SYMBOL(register_inet6addr_notifier);
-EXPORT_SYMBOL(unregister_inet6addr_notifier);
-#include <net/ip6_route.h>
-EXPORT_SYMBOL(ip6_route_output);
-#ifdef CONFIG_NETFILTER
-EXPORT_SYMBOL(ip6_route_me_harder);
-#endif
-#endif
-#if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE)
+#if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE) || defined (CONFIG_IP_SCTP_MODULE)
 /* inet functions common to v4 and v6 */
 EXPORT_SYMBOL(inet_release);
 EXPORT_SYMBOL(inet_stream_connect);
@@ -422,6 +413,14 @@
 
 EXPORT_SYMBOL(tcp_read_sock);
 
+#ifdef CONFIG_IP_SCTP_MODULE
+EXPORT_SYMBOL(ip_setsockopt);
+EXPORT_SYMBOL(ip_getsockopt);
+EXPORT_SYMBOL(inet_ioctl);
+EXPORT_SYMBOL(inet_bind);
+EXPORT_SYMBOL(inet_getname);
+#endif /* CONFIG_IP_SCTP_MODULE */
+
 EXPORT_SYMBOL(netlink_set_err);
 EXPORT_SYMBOL(netlink_broadcast);
 EXPORT_SYMBOL(netlink_unicast);
diff -urN linux-2.4.23-pre5/net/sched/sch_generic.c linux-2.4.23-pre6/net/sched/sch_generic.c
--- linux-2.4.23-pre5/net/sched/sch_generic.c	2002-11-28 15:53:16.000000000 -0800
+++ linux-2.4.23-pre6/net/sched/sch_generic.c	2003-10-01 10:38:59.000000000 -0700
@@ -7,7 +7,7 @@
  *		2 of the License, or (at your option) any later version.
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
- *              Jamal Hadi Salim, <hadi@nortelnetworks.com> 990601
+ *              Jamal Hadi Salim, <hadi@cyberus.ca> 990601
  *              - Ingress support
  */
 
@@ -283,6 +283,8 @@
 	if (list->qlen <= qdisc->dev->tx_queue_len) {
 		__skb_queue_tail(list, skb);
 		qdisc->q.qlen++;
+		qdisc->stats.bytes += skb->len;
+		qdisc->stats.packets++;
 		return 0;
 	}
 	qdisc->stats.drops++;
@@ -331,6 +333,21 @@
 	qdisc->q.qlen = 0;
 }
 
+static int pfifo_fast_dump(struct Qdisc *qdisc, struct sk_buff *skb)
+{
+	unsigned char	 *b = skb->tail;
+	struct tc_prio_qopt opt;
+
+	opt.bands = 3; 
+	memcpy(&opt.priomap, prio2band, TC_PRIO_MAX+1);
+	RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
+	return skb->len;
+
+rtattr_failure:
+	skb_trim(skb, b - skb->data);
+	return -1;
+}
+
 static int pfifo_fast_init(struct Qdisc *qdisc, struct rtattr *opt)
 {
 	int i;
@@ -358,6 +375,10 @@
 
 	pfifo_fast_init,
 	pfifo_fast_reset,
+	NULL,
+	NULL,
+	pfifo_fast_dump,
+
 };
 
 struct Qdisc * qdisc_create_dflt(struct net_device *dev, struct Qdisc_ops *ops)
@@ -406,7 +427,6 @@
 
 	dev = qdisc->dev;
 
-#ifdef CONFIG_NET_SCHED
 	if (dev) {
 		struct Qdisc *q, **qp;
 		for (qp = &qdisc->dev->qdisc_list; (q=*qp) != NULL; qp = &q->next) {
@@ -419,7 +439,6 @@
 #ifdef CONFIG_NET_ESTIMATOR
 	qdisc_kill_estimator(&qdisc->stats);
 #endif
-#endif
 	if (ops->reset)
 		ops->reset(qdisc);
 	if (ops->destroy)
@@ -445,6 +464,12 @@
 				printk(KERN_INFO "%s: activation failed\n", dev->name);
 				return;
 			}
+
+			write_lock(&qdisc_tree_lock);
+			qdisc->next = dev->qdisc_list;
+			dev->qdisc_list = qdisc;
+			write_unlock(&qdisc_tree_lock);
+
 		} else {
 			qdisc =  &noqueue_qdisc;
 		}
diff -urN linux-2.4.23-pre5/net/sctp/Config.in linux-2.4.23-pre6/net/sctp/Config.in
--- linux-2.4.23-pre5/net/sctp/Config.in	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/Config.in	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,20 @@
+#
+# SCTP configuration
+#
+mainmenu_option next_comment
+comment '   SCTP Configuration (EXPERIMENTAL)'
+
+if [ "$CONFIG_IPV6" != "n" ]; then
+   define_bool CONFIG_IPV6_SCTP__ $CONFIG_IPV6
+else
+   define_bool CONFIG_IPV6_SCTP__ y
+fi
+
+dep_tristate '  The SCTP Protocol (EXPERIMENTAL)' CONFIG_IP_SCTP $CONFIG_IPV6_SCTP__
+if [ "$CONFIG_IP_SCTP" != "n" ]; then
+   bool '    SCTP: Use old checksum (Adler-32)' CONFIG_SCTP_ADLER32
+   bool '    SCTP: Debug messages' CONFIG_SCTP_DBG_MSG
+   bool '    SCTP: Debug object counts' CONFIG_SCTP_DBG_OBJCNT
+fi
+
+endmenu
diff -urN linux-2.4.23-pre5/net/sctp/Makefile linux-2.4.23-pre6/net/sctp/Makefile
--- linux-2.4.23-pre5/net/sctp/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/Makefile	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,27 @@
+#
+# Makefile for SCTP support code.
+#
+
+O_TARGET := sctp.o
+
+obj-$(CONFIG_IP_SCTP) += sctp.o
+
+obj-y := endpointola.o output.o sm_make_chunk.o associola.o hashdriver.o \
+	outqueue.o sm_sideeffect.o transport.o bind_addr.o input.o primitive.o \
+	sm_statefuns.o tsnmap.o command.o inqueue.o proc.o sm_statetable.o \
+	ulpevent.o protocol.o socket.o ulpqueue.o debug.o sla1.o ssnmap.o
+
+ifeq ($(CONFIG_SCTP_ADLER32), y)
+obj-y += adler32.o
+else
+obj-y += crc32c.o
+endif
+
+obj-$(CONFIG_SCTP_DBG_OBJCNT) += objcnt.o
+obj-$(CONFIG_SYSCTL) += sysctl.o
+
+obj-$(subst m,y,$(CONFIG_IPV6))	+= ipv6.o
+
+sctp-objs := $(obj-y)
+
+include $(TOPDIR)/Rules.make
diff -urN linux-2.4.23-pre5/net/sctp/adler32.c linux-2.4.23-pre6/net/sctp/adler32.c
--- linux-2.4.23-pre5/net/sctp/adler32.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/adler32.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,171 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2003 International Business Machines, Corp.
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * This file has direct heritage from the SCTP user-level reference
+ * implementation by R. Stewart, et al.  These functions implement the
+ * Adler-32 algorithm as specified by RFC 2960.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    Randall Stewart <rstewar1@email.mot.com>
+ *    Ken Morneau     <kmorneau@cisco.com>
+ *    Qiaobing Xie    <qxie1@email.mot.com>
+ *    Sridhar Samudrala <sri@us.ibm.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+/* This is an entry point for external calls
+ * Define this function in the header file. This is
+ * direct from rfc1950, ...
+ *
+ * The following C code computes the Adler-32 checksum of a data buffer.
+ * It is written for clarity, not for speed.  The sample code is in the
+ * ANSI C programming language. Non C users may find it easier to read
+ * with these hints:
+ *
+ *    &      Bitwise AND operator.
+ *    >>     Bitwise right shift operator. When applied to an
+ *           unsigned quantity, as here, right shift inserts zero bit(s)
+ *           at the left.
+ *    <<     Bitwise left shift operator. Left shift inserts zero
+ *           bit(s) at the right.
+ *    ++     "n++" increments the variable n.
+ *    %      modulo operator: a % b is the remainder of a divided by b.
+ *
+ * Well, the above is a bit of a lie, I have optimized this a small
+ * tad, but I have commented the original lines below
+ */
+
+#include <linux/types.h>
+#include <net/sctp/sctp.h>
+
+#define BASE 65521 /* largest prime smaller than 65536 */
+
+
+/* Performance work as shown this pig to be the
+ * worst CPU wise guy. I have done what I could think
+ * of on my flight to Australia but I am sure some
+ * clever assembly could speed this up, but of
+ * course this would require the dreaded #ifdef's for
+ * architecture. If you can speed this up more, pass
+ * it back and we will incorporate it :-)
+ */
+
+unsigned long update_adler32(unsigned long adler,
+			     unsigned char *buf, int len)
+{
+	__u32 s1 = adler & 0xffff;
+	__u32 s2 = (adler >> 16) & 0xffff;
+        int n;
+
+	for (n = 0; n < len; n++,buf++) {
+		/* s1 = (s1 + buf[n]) % BASE */
+		/* first we add */
+		s1 = (s1 + *buf);
+
+		/* Now if we need to, we do a mod by
+		 * subtracting. It seems a bit faster
+		 * since I really will only ever do
+		 * one subtract at the MOST, since buf[n]
+		 * is a max of 255.
+		 */
+		if (s1 >= BASE)
+			s1 -= BASE;
+
+		/* s2 = (s2 + s1) % BASE */
+		/* first we add */
+		s2 = (s2 + s1);
+
+		/* again, it is more efficient (it seems) to
+		 * subtract since the most s2 will ever be
+		 * is (BASE-1 + BASE-1) in the worse case.
+		 * This would then be (2 * BASE) - 2, which
+		 * will still only do one subtract. On Intel
+		 * this is much better to do this way and
+		 * avoid the divide. Have not -pg'd on
+		 * sparc.
+		 */
+		if (s2 >= BASE) {
+			/*      s2 %= BASE;*/
+			s2 -= BASE;
+		}
+	}
+
+	/* Return the adler32 of the bytes buf[0..len-1] */
+	return (s2 << 16) + s1;
+}
+
+__u32 sctp_start_cksum(__u8 *ptr, __u16 count)
+{
+	/*
+	 * Update a running Adler-32 checksum with the bytes
+	 * buf[0..len-1] and return the updated checksum. The Adler-32
+	 * checksum should be initialized to 1.
+	 */
+	__u32 adler = 1L;
+	__u32 zero = 0L;
+
+	/* Calculate the CRC up to the checksum field. */
+	adler = update_adler32(adler, ptr,
+			       sizeof(struct sctphdr) - sizeof(__u32));
+	/* Skip over the checksum field. */
+	adler = update_adler32(adler, (unsigned char *) &zero,
+			       sizeof(__u32));
+	ptr += sizeof(struct sctphdr);
+	count -= sizeof(struct sctphdr);
+
+	/* Calculate the rest of the Adler-32. */
+	adler = update_adler32(adler, ptr, count);
+
+        return adler;
+}
+
+__u32 sctp_update_cksum(__u8 *ptr, __u16 count, __u32 adler)
+{
+	adler = update_adler32(adler, ptr, count);
+
+	return adler;
+}
+
+__u32 sctp_update_copy_cksum(__u8 *to, __u8 *from, __u16 count, __u32 adler)
+{
+	/* Its not worth it to try harder.  Adler32 is obsolescent. */
+	adler = update_adler32(adler, from, count);
+	memcpy(to, from, count);
+
+	return adler;
+}
+
+__u32 sctp_end_cksum(__u32 adler)
+{
+	return adler;
+}
diff -urN linux-2.4.23-pre5/net/sctp/associola.c linux-2.4.23-pre6/net/sctp/associola.c
--- linux-2.4.23-pre5/net/sctp/associola.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/associola.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,1106 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001-2003 International Business Machines Corp.
+ * Copyright (c) 2001 Intel Corp.
+ * Copyright (c) 2001 La Monte H.P. Yarroll
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * This module provides the abstraction for an SCTP association.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Karl Knutson          <karl@athena.chicago.il.us>
+ *    Jon Grimm             <jgrimm@us.ibm.com>
+ *    Xingang Guo           <xingang.guo@intel.com>
+ *    Hui Huang             <hui.huang@nokia.com>
+ *    Sridhar Samudrala	    <sri@us.ibm.com>
+ *    Daisy Chang	    <daisyc@us.ibm.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+
+#include <linux/slab.h>
+#include <linux/in.h>
+#include <net/ipv6.h>
+#include <net/sctp/sctp.h>
+
+/* Forward declarations for internal functions. */
+static void sctp_assoc_bh_rcv(struct sctp_association *asoc);
+
+
+/* 1st Level Abstractions. */
+
+/* Allocate and initialize a new association */
+struct sctp_association *sctp_association_new(const struct sctp_endpoint *ep,
+					 const struct sock *sk,
+					 sctp_scope_t scope, int gfp)
+{
+	struct sctp_association *asoc;
+
+	asoc = t_new(struct sctp_association, gfp);
+	if (!asoc)
+		goto fail;
+
+	if (!sctp_association_init(asoc, ep, sk, scope, gfp))
+		goto fail_init;
+
+	asoc->base.malloced = 1;
+	SCTP_DBG_OBJCNT_INC(assoc);
+
+	return asoc;
+
+fail_init:
+	kfree(asoc);
+fail:
+	return NULL;
+}
+
+/* Initialize a new association from provided memory. */
+struct sctp_association *sctp_association_init(struct sctp_association *asoc,
+					  const struct sctp_endpoint *ep,
+					  const struct sock *sk,
+					  sctp_scope_t scope,
+					  int gfp)
+{
+	struct sctp_opt *sp;
+	struct sctp_protocol *proto = sctp_get_protocol();
+	int i;
+
+	/* Retrieve the SCTP per socket area.  */
+	sp = sctp_sk((struct sock *)sk);
+
+	/* Init all variables to a known value.  */
+	memset(asoc, 0, sizeof(struct sctp_association));
+
+	/* Discarding const is appropriate here.  */
+	asoc->ep = (struct sctp_endpoint *)ep;
+	sctp_endpoint_hold(asoc->ep);
+
+	/* Hold the sock.  */
+	asoc->base.sk = (struct sock *)sk;
+	sock_hold(asoc->base.sk);
+
+	/* Initialize the common base substructure.  */
+	asoc->base.type = SCTP_EP_TYPE_ASSOCIATION;
+
+	/* Initialize the object handling fields.  */
+	atomic_set(&asoc->base.refcnt, 1);
+	asoc->base.dead = 0;
+	asoc->base.malloced = 0;
+
+	/* Initialize the bind addr area.  */
+	sctp_bind_addr_init(&asoc->base.bind_addr, ep->base.bind_addr.port);
+	asoc->base.addr_lock = RW_LOCK_UNLOCKED;
+
+	asoc->state = SCTP_STATE_CLOSED;
+	asoc->state_timestamp = jiffies;
+
+	/* Set things that have constant value.  */
+	asoc->cookie_life.tv_sec = sctp_proto.valid_cookie_life / HZ;
+	asoc->cookie_life.tv_usec = (sctp_proto.valid_cookie_life % HZ) *
+					1000000L / HZ;
+
+	asoc->pmtu = 0;
+	asoc->frag_point = 0;
+
+	/* Initialize the default association max_retrans and RTO values.  */
+	asoc->max_retrans = proto->max_retrans_association;
+	asoc->rto_initial = proto->rto_initial;
+	asoc->rto_max = proto->rto_max;
+	asoc->rto_min = proto->rto_min;
+
+	asoc->overall_error_threshold = 0;
+	asoc->overall_error_count = 0;
+
+	/* Initialize the maximum mumber of new data packets that can be sent
+	 * in a burst.
+	 */
+	asoc->max_burst = proto->max_burst;
+
+	/* Copy things from the endpoint.  */
+	for (i = SCTP_EVENT_TIMEOUT_NONE; i < SCTP_NUM_TIMEOUT_TYPES; ++i) {
+		asoc->timeouts[i] = ep->timeouts[i];
+		init_timer(&asoc->timers[i]);
+		asoc->timers[i].function = sctp_timer_events[i];
+		asoc->timers[i].data = (unsigned long) asoc;
+	}
+
+	/* Pull default initialization values from the sock options.
+	 * Note: This assumes that the values have already been
+	 * validated in the sock.
+	 */
+	asoc->c.sinit_max_instreams = sp->initmsg.sinit_max_instreams;
+	asoc->c.sinit_num_ostreams  = sp->initmsg.sinit_num_ostreams;
+	asoc->max_init_attempts	= sp->initmsg.sinit_max_attempts;
+	asoc->max_init_timeo    = sp->initmsg.sinit_max_init_timeo * HZ;
+
+	/* Allocate storage for the ssnmap after the inbound and outbound
+	 * streams have been negotiated during Init.
+	 */
+	asoc->ssnmap = NULL;
+
+	/* Set the local window size for receive.
+	 * This is also the rcvbuf space per association.
+	 * RFC 6 - A SCTP receiver MUST be able to receive a minimum of
+	 * 1500 bytes in one SCTP packet.
+	 */
+	if (sk->rcvbuf < SCTP_DEFAULT_MINWINDOW)
+		asoc->rwnd = SCTP_DEFAULT_MINWINDOW;
+	else
+		asoc->rwnd = sk->rcvbuf;
+
+	asoc->a_rwnd = asoc->rwnd;
+
+	asoc->rwnd_over = 0;
+
+	/* Use my own max window until I learn something better.  */
+	asoc->peer.rwnd = SCTP_DEFAULT_MAXWINDOW;
+
+	/* Set the sndbuf size for transmit.  */
+	asoc->sndbuf_used = 0;
+
+	init_waitqueue_head(&asoc->wait);
+
+	asoc->c.my_vtag = sctp_generate_tag(ep);
+	asoc->peer.i.init_tag = 0;     /* INIT needs a vtag of 0. */
+	asoc->c.peer_vtag = 0;
+	asoc->c.my_ttag   = 0;
+	asoc->c.peer_ttag = 0;
+
+	asoc->c.initial_tsn = sctp_generate_tsn(ep);
+
+	asoc->next_tsn = asoc->c.initial_tsn;
+
+	asoc->ctsn_ack_point = asoc->next_tsn - 1;
+	asoc->highest_sacked = asoc->ctsn_ack_point;
+	asoc->last_cwr_tsn = asoc->ctsn_ack_point;
+	asoc->unack_data = 0;
+
+	SCTP_DEBUG_PRINTK("myctsnap for %s INIT as 0x%x.\n",
+			  asoc->ep->debug_name,
+			  asoc->ctsn_ack_point);
+
+	/* ADDIP Section 4.1 Asconf Chunk Procedures
+	 *
+	 * When an endpoint has an ASCONF signaled change to be sent to the
+	 * remote endpoint it should do the following:
+	 * ...
+	 * A2) a serial number should be assigned to the chunk. The serial
+	 * number should be a monotonically increasing number. All serial
+	 * numbers are defined to be initialized at the start of the
+	 * association to the same value as the initial TSN.
+	 */
+	asoc->addip_serial = asoc->c.initial_tsn;
+
+	/* Make an empty list of remote transport addresses.  */
+	INIT_LIST_HEAD(&asoc->peer.transport_addr_list);
+
+	/* RFC 2960 5.1 Normal Establishment of an Association
+	 *
+	 * After the reception of the first data chunk in an
+	 * association the endpoint must immediately respond with a
+	 * sack to acknowledge the data chunk.  Subsequent
+	 * acknowledgements should be done as described in Section
+	 * 6.2.
+	 *
+	 * [We implement this by telling a new association that it
+	 * already received one packet.]
+	 */
+	asoc->peer.sack_needed = 1;
+
+	/* Create an input queue.  */
+	sctp_inq_init(&asoc->base.inqueue);
+	sctp_inq_set_th_handler(&asoc->base.inqueue,
+				    (void (*)(void *))sctp_assoc_bh_rcv,
+				    asoc);
+
+	/* Create an output queue.  */
+	sctp_outq_init(asoc, &asoc->outqueue);
+	sctp_outq_set_output_handlers(&asoc->outqueue,
+				      sctp_packet_init,
+				      sctp_packet_config,
+				      sctp_packet_append_chunk,
+				      sctp_packet_transmit_chunk,
+				      sctp_packet_transmit);
+
+	if (NULL == sctp_ulpq_init(&asoc->ulpq, asoc))
+		goto fail_init;
+
+	/* Set up the tsn tracking. */
+	sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_SIZE, 0);
+
+	skb_queue_head_init(&asoc->addip_chunks);
+
+	asoc->need_ecne = 0;
+
+	asoc->debug_name = "unnamedasoc";
+	asoc->eyecatcher = SCTP_ASSOC_EYECATCHER;
+
+	/* Assume that peer would support both address types unless we are
+	 * told otherwise.
+	 */
+	asoc->peer.ipv4_address = 1;
+	asoc->peer.ipv6_address = 1;
+	INIT_LIST_HEAD(&asoc->asocs);
+
+	asoc->autoclose = sp->autoclose;
+
+	return asoc;
+
+fail_init:
+	sctp_endpoint_put(asoc->ep);
+	sock_put(asoc->base.sk);
+	return NULL;
+}
+
+/* Free this association if possible.  There may still be users, so
+ * the actual deallocation may be delayed.
+ */
+void sctp_association_free(struct sctp_association *asoc)
+{
+	struct sock *sk = asoc->base.sk;
+	struct sctp_transport *transport;
+	struct list_head *pos, *temp;
+	int i;
+
+	list_del(&asoc->asocs);
+
+	/* Decrement the backlog value for a TCP-style listening socket. */
+	if ((SCTP_SOCKET_TCP == sctp_sk(sk)->type) &&
+	    (SCTP_SS_LISTENING == sk->state))
+		sk->ack_backlog--;
+
+	/* Mark as dead, so other users can know this structure is
+	 * going away.
+	 */
+	asoc->base.dead = 1;
+
+	/* Dispose of any data lying around in the outqueue. */
+	sctp_outq_free(&asoc->outqueue);
+
+	/* Dispose of any pending messages for the upper layer. */
+	sctp_ulpq_free(&asoc->ulpq);
+
+	/* Dispose of any pending chunks on the inqueue. */
+	sctp_inq_free(&asoc->base.inqueue);
+
+	/* Free ssnmap storage. */
+	sctp_ssnmap_free(asoc->ssnmap);
+
+	/* Clean up the bound address list. */
+	sctp_bind_addr_free(&asoc->base.bind_addr);
+
+	/* Do we need to go through all of our timers and
+	 * delete them?   To be safe we will try to delete all, but we
+	 * should be able to go through and make a guess based
+	 * on our state.
+	 */
+	for (i = SCTP_EVENT_TIMEOUT_NONE; i < SCTP_NUM_TIMEOUT_TYPES; ++i) {
+		if (timer_pending(&asoc->timers[i]) &&
+		    del_timer(&asoc->timers[i]))
+			sctp_association_put(asoc);
+	}
+
+	/* Free peer's cached cookie. */
+	if (asoc->peer.cookie) {
+		kfree(asoc->peer.cookie);
+	}
+
+	/* Release the transport structures. */
+	list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
+		transport = list_entry(pos, struct sctp_transport, transports);
+		list_del(pos);
+		sctp_transport_free(transport);
+	}
+
+	asoc->eyecatcher = 0;
+
+	sctp_association_put(asoc);
+}
+
+/* Cleanup and free up an association. */
+static void sctp_association_destroy(struct sctp_association *asoc)
+{
+	SCTP_ASSERT(asoc->base.dead, "Assoc is not dead", return);
+
+	sctp_endpoint_put(asoc->ep);
+	sock_put(asoc->base.sk);
+
+	if (asoc->base.malloced) {
+		kfree(asoc);
+		SCTP_DBG_OBJCNT_DEC(assoc);
+	}
+}
+
+/* Change the primary destination address for the peer. */
+void sctp_assoc_set_primary(struct sctp_association *asoc,
+			    struct sctp_transport *transport)
+{
+	asoc->peer.primary_path = transport;
+
+	/* Set a default msg_name for events. */
+	memcpy(&asoc->peer.primary_addr, &transport->ipaddr,
+	       sizeof(union sctp_addr));
+
+	/* If the primary path is changing, assume that the
+	 * user wants to use this new path.
+	 */
+	if (transport->active)
+		asoc->peer.active_path = transport;
+}
+
+/* Add a transport address to an association.  */
+struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
+					   const union sctp_addr *addr,
+					   int gfp)
+{
+	struct sctp_transport *peer;
+	struct sctp_opt *sp;
+	unsigned short port;
+
+	/* AF_INET and AF_INET6 share common port field. */
+	port = addr->v4.sin_port;
+
+	/* Set the port if it has not been set yet.  */
+	if (0 == asoc->peer.port) {
+		asoc->peer.port = port;
+	}
+
+	/* Check to see if this is a duplicate. */
+	peer = sctp_assoc_lookup_paddr(asoc, addr);
+	if (peer)
+		return peer;
+
+	peer = sctp_transport_new(addr, gfp);
+	if (!peer)
+		return NULL;
+
+	sctp_transport_set_owner(peer, asoc);
+
+	/* Initialize the pmtu of the transport. */
+	sctp_transport_pmtu(peer);
+
+	/* If this is the first transport addr on this association,
+	 * initialize the association PMTU to the peer's PMTU.
+	 * If not and the current association PMTU is higher than the new
+	 * peer's PMTU, reset the association PMTU to the new peer's PMTU.
+	 */
+	if (asoc->pmtu)
+		asoc->pmtu = min_t(int, peer->pmtu, asoc->pmtu);
+	else
+		asoc->pmtu = peer->pmtu;
+
+	SCTP_DEBUG_PRINTK("sctp_assoc_add_peer:association %p PMTU set to "
+			  "%d\n", asoc, asoc->pmtu);
+
+	asoc->frag_point = sctp_frag_point(asoc->pmtu);
+
+	/* The asoc->peer.port might not be meaningful yet, but
+	 * initialize the packet structure anyway.
+	 */
+	(asoc->outqueue.init_output)(&peer->packet,
+				     peer,
+				     asoc->base.bind_addr.port,
+				     asoc->peer.port);
+
+	/* 7.2.1 Slow-Start
+	 *
+	 * o The initial cwnd before data transmission or after a
+	 *   sufficiently long idle period MUST be <= 2*MTU.
+	 *
+	 * o The initial value of ssthresh MAY be arbitrarily high
+	 *   (for example, implementations MAY use the size of the
+	 *   receiver advertised window).
+	 */
+	peer->cwnd = asoc->pmtu * 2;
+
+	/* At this point, we may not have the receiver's advertised window,
+	 * so initialize ssthresh to the default value and it will be set
+	 * later when we process the INIT.
+	 */
+	peer->ssthresh = SCTP_DEFAULT_MAXWINDOW;
+
+	peer->partial_bytes_acked = 0;
+	peer->flight_size = 0;
+
+	peer->error_threshold = peer->max_retrans;
+
+	/* Update the overall error threshold value of the association
+	 * taking the new peer's error threshold into account.
+	 */
+	asoc->overall_error_threshold =
+		min(asoc->overall_error_threshold + peer->error_threshold,
+		    asoc->max_retrans);
+
+	/* By default, enable heartbeat for peer address. */
+	peer->hb_allowed = 1;
+
+	/* Initialize the peer's heartbeat interval based on the
+	 * sock configured value.
+	 */
+	sp = sctp_sk(asoc->base.sk);
+	peer->hb_interval = sp->paddrparam.spp_hbinterval * HZ;
+
+	/* Attach the remote transport to our asoc.  */
+	list_add_tail(&peer->transports, &asoc->peer.transport_addr_list);
+
+	/* If we do not yet have a primary path, set one.  */
+	if (NULL == asoc->peer.primary_path) {
+		sctp_assoc_set_primary(asoc, peer);
+		asoc->peer.retran_path = peer;
+	}
+
+	if (asoc->peer.active_path == asoc->peer.retran_path)
+		asoc->peer.retran_path = peer;
+
+	return peer;
+}
+
+/* Lookup a transport by address. */
+struct sctp_transport *sctp_assoc_lookup_paddr(
+					const struct sctp_association *asoc,
+					const union sctp_addr *address)
+{
+	struct sctp_transport *t;
+	struct list_head *pos;
+
+	/* Cycle through all transports searching for a peer address. */
+
+	list_for_each(pos, &asoc->peer.transport_addr_list) {
+		t = list_entry(pos, struct sctp_transport, transports);
+		if (sctp_cmp_addr_exact(address, &t->ipaddr))
+			return t;
+	}
+
+	return NULL;
+}
+
+/* Engage in transport control operations.
+ * Mark the transport up or down and send a notification to the user.
+ * Select and update the new active and retran paths.
+ */
+void sctp_assoc_control_transport(struct sctp_association *asoc,
+				  struct sctp_transport *transport,
+				  sctp_transport_cmd_t command,
+				  sctp_sn_error_t error)
+{
+	struct sctp_transport *t = NULL;
+	struct sctp_transport *first;
+	struct sctp_transport *second;
+	struct sctp_ulpevent *event;
+	struct list_head *pos;
+	int spc_state = 0;
+
+	/* Record the transition on the transport.  */
+	switch (command) {
+	case SCTP_TRANSPORT_UP:
+		transport->active = 1;
+		spc_state = ADDRESS_AVAILABLE;
+		break;
+
+	case SCTP_TRANSPORT_DOWN:
+		transport->active = 0;
+		spc_state = ADDRESS_UNREACHABLE;
+		break;
+
+	default:
+		return;
+	};
+
+	/* Generate and send a SCTP_PEER_ADDR_CHANGE notification to the
+	 * user.
+	 */
+	event = sctp_ulpevent_make_peer_addr_change(asoc,
+				(struct sockaddr_storage *) &transport->ipaddr,
+				0, spc_state, error, GFP_ATOMIC);
+	if (event)
+		sctp_ulpq_tail_event(&asoc->ulpq, event);
+
+	/* Select new active and retran paths. */
+
+	/* Look for the two most recently used active transports.
+	 *
+	 * This code produces the wrong ordering whenever jiffies
+	 * rolls over, but we still get usable transports, so we don't
+	 * worry about it.
+	 */
+	first = NULL; second = NULL;
+
+	list_for_each(pos, &asoc->peer.transport_addr_list) {
+		t = list_entry(pos, struct sctp_transport, transports);
+
+		if (!t->active)
+			continue;
+		if (!first || t->last_time_heard > first->last_time_heard) {
+			second = first;
+			first = t;
+		}
+		if (!second || t->last_time_heard > second->last_time_heard)
+			second = t;
+	}
+
+	/* RFC 2960 6.4 Multi-Homed SCTP Endpoints
+	 *
+	 * By default, an endpoint should always transmit to the
+	 * primary path, unless the SCTP user explicitly specifies the
+	 * destination transport address (and possibly source
+	 * transport address) to use.
+	 *
+	 * [If the primary is active but not most recent, bump the most
+	 * recently used transport.]
+	 */
+	if (asoc->peer.primary_path->active &&
+	    first != asoc->peer.primary_path) {
+		second = first;
+		first = asoc->peer.primary_path;
+	}
+
+	/* If we failed to find a usable transport, just camp on the
+	 * primary, even if it is inactive.
+	 */
+	if (NULL == first) {
+		first = asoc->peer.primary_path;
+		second = asoc->peer.primary_path;
+	}
+
+	/* Set the active and retran transports.  */
+	asoc->peer.active_path = first;
+	asoc->peer.retran_path = second;
+}
+
+/* Hold a reference to an association. */
+void sctp_association_hold(struct sctp_association *asoc)
+{
+	atomic_inc(&asoc->base.refcnt);
+}
+
+/* Release a reference to an association and cleanup
+ * if there are no more references.
+ */
+void sctp_association_put(struct sctp_association *asoc)
+{
+	if (atomic_dec_and_test(&asoc->base.refcnt))
+		sctp_association_destroy(asoc);
+}
+
+/* Allocate the next TSN, Transmission Sequence Number, for the given
+ * association.
+ */
+__u32 sctp_association_get_next_tsn(struct sctp_association *asoc)
+{
+	/* From Section 1.6 Serial Number Arithmetic:
+	 * Transmission Sequence Numbers wrap around when they reach
+	 * 2**32 - 1.  That is, the next TSN a DATA chunk MUST use
+	 * after transmitting TSN = 2*32 - 1 is TSN = 0.
+	 */
+	__u32 retval = asoc->next_tsn;
+	asoc->next_tsn++;
+	asoc->unack_data++;
+
+	return retval;
+}
+
+/* Allocate 'num' TSNs by incrementing the association's TSN by num. */
+__u32 sctp_association_get_tsn_block(struct sctp_association *asoc, int num)
+{
+	__u32 retval = asoc->next_tsn;
+
+	asoc->next_tsn += num;
+	asoc->unack_data += num;
+
+	return retval;
+}
+
+
+/* Compare two addresses to see if they match.  Wildcard addresses
+ * only match themselves.
+ */
+int sctp_cmp_addr_exact(const union sctp_addr *ss1,
+			const union sctp_addr *ss2)
+{
+	struct sctp_af *af;
+
+	af = sctp_get_af_specific(ss1->sa.sa_family);
+	if (unlikely(!af))
+		return 0;
+
+	return af->cmp_addr(ss1, ss2);
+}
+
+/* Return an ecne chunk to get prepended to a packet.
+ * Note:  We are sly and return a shared, prealloced chunk.  FIXME:
+ * No we don't, but we could/should.
+ */
+sctp_chunk_t *sctp_get_ecne_prepend(struct sctp_association *asoc)
+{
+	struct sctp_chunk *chunk;
+
+	/* Send ECNE if needed.
+	 * Not being able to allocate a chunk here is not deadly.
+	 */
+	if (asoc->need_ecne)
+		chunk = sctp_make_ecne(asoc, asoc->last_ecne_tsn);
+	else
+		chunk = NULL;
+
+	return chunk;
+}
+
+/* Use this function for the packet prepend callback when no ECNE
+ * packet is desired (e.g. some packets don't like to be bundled).
+ */
+sctp_chunk_t *sctp_get_no_prepend(struct sctp_association *asoc)
+{
+	return NULL;
+}
+
+/*
+ * Find which transport this TSN was sent on.
+ */
+struct sctp_transport *sctp_assoc_lookup_tsn(struct sctp_association *asoc, __u32 tsn)
+{
+	struct sctp_transport *active;
+	struct sctp_transport *match;
+	struct list_head *entry, *pos;
+	struct sctp_transport *transport;
+	sctp_chunk_t *chunk;
+	__u32 key = htonl(tsn);
+
+	match = NULL;
+
+	/*
+	 * FIXME: In general, find a more efficient data structure for
+	 * searching.
+	 */
+
+	/*
+	 * The general strategy is to search each transport's transmitted
+	 * list.   Return which transport this TSN lives on.
+	 *
+	 * Let's be hopeful and check the active_path first.
+	 * Another optimization would be to know if there is only one
+	 * outbound path and not have to look for the TSN at all.
+	 *
+	 */
+
+	active = asoc->peer.active_path;
+
+	list_for_each(entry, &active->transmitted) {
+		chunk = list_entry(entry, sctp_chunk_t, transmitted_list);
+
+		if (key == chunk->subh.data_hdr->tsn) {
+			match = active;
+			goto out;
+		}
+	}
+
+	/* If not found, go search all the other transports. */
+	list_for_each(pos, &asoc->peer.transport_addr_list) {
+		transport = list_entry(pos, struct sctp_transport, transports);
+
+		if (transport == active)
+			break;
+		list_for_each(entry, &transport->transmitted) {
+			chunk = list_entry(entry, sctp_chunk_t,
+					   transmitted_list);
+			if (key == chunk->subh.data_hdr->tsn) {
+				match = transport;
+				goto out;
+			}
+		}
+	}
+out:
+	return match;
+}
+
+/* Is this the association we are looking for? */
+struct sctp_transport *sctp_assoc_is_match(struct sctp_association *asoc,
+					   const union sctp_addr *laddr,
+					   const union sctp_addr *paddr)
+{
+	struct sctp_transport *transport;
+
+	sctp_read_lock(&asoc->base.addr_lock);
+
+	if ((asoc->base.bind_addr.port == laddr->v4.sin_port) &&
+	    (asoc->peer.port == paddr->v4.sin_port)) {
+		transport = sctp_assoc_lookup_paddr(asoc, paddr);
+		if (!transport)
+			goto out;
+
+		if (sctp_bind_addr_match(&asoc->base.bind_addr, laddr,
+					 sctp_sk(asoc->base.sk)))
+			goto out;
+	}
+	transport = NULL;
+
+out:
+	sctp_read_unlock(&asoc->base.addr_lock);
+	return transport;
+}
+
+/* Do delayed input processing.  This is scheduled by sctp_rcv(). */
+static void sctp_assoc_bh_rcv(struct sctp_association *asoc)
+{
+	struct sctp_endpoint *ep;
+	sctp_chunk_t *chunk;
+	struct sock *sk;
+	struct sctp_inq *inqueue;
+	int state, subtype;
+	sctp_assoc_t associd = sctp_assoc2id(asoc);
+	int error = 0;
+
+	/* The association should be held so we should be safe. */
+	ep = asoc->ep;
+	sk = asoc->base.sk;
+
+	inqueue = &asoc->base.inqueue;
+	while (NULL != (chunk = sctp_inq_pop(inqueue))) {
+		state = asoc->state;
+		subtype = chunk->chunk_hdr->type;
+
+		/* Remember where the last DATA chunk came from so we
+		 * know where to send the SACK.
+		 */
+		if (sctp_chunk_is_data(chunk))
+			asoc->peer.last_data_from = chunk->transport;
+		else
+			SCTP_INC_STATS(SctpInCtrlChunks);
+
+		if (chunk->transport)
+			chunk->transport->last_time_heard = jiffies;
+
+		/* Run through the state machine. */
+		error = sctp_do_sm(SCTP_EVENT_T_CHUNK, SCTP_ST_CHUNK(subtype),
+				   state, ep, asoc, chunk, GFP_ATOMIC);
+
+		/* Check to see if the association is freed in response to
+		 * the incoming chunk.  If so, get out of the while loop.
+		 */
+		if (!sctp_id2assoc(sk, associd))
+			break;
+
+		/* If there is an error on chunk, discard this packet. */
+		if (error && chunk)
+			chunk->pdiscard = 1;
+	}
+
+}
+
+/* This routine moves an association from its old sk to a new sk.  */
+void sctp_assoc_migrate(struct sctp_association *assoc, struct sock *newsk)
+{
+	struct sctp_opt *newsp = sctp_sk(newsk);
+	struct sock *oldsk = assoc->base.sk;
+
+	/* Delete the association from the old endpoint's list of
+	 * associations.
+	 */
+	list_del(&assoc->asocs);
+
+	/* Decrement the backlog value for a TCP-style socket. */
+	if (SCTP_SOCKET_TCP == sctp_sk(oldsk)->type)
+		oldsk->ack_backlog--;
+
+	/* Release references to the old endpoint and the sock.  */
+	sctp_endpoint_put(assoc->ep);
+	sock_put(assoc->base.sk);
+
+	/* Get a reference to the new endpoint.  */
+	assoc->ep = newsp->ep;
+	sctp_endpoint_hold(assoc->ep);
+
+	/* Get a reference to the new sock.  */
+	assoc->base.sk = newsk;
+	sock_hold(assoc->base.sk);
+
+	/* Add the association to the new endpoint's list of associations.  */
+	sctp_endpoint_add_asoc(newsp->ep, assoc);
+}
+
+/* Update an association (possibly from unexpected COOKIE-ECHO processing).  */
+void sctp_assoc_update(struct sctp_association *asoc, struct sctp_association *new)
+{
+	/* Copy in new parameters of peer. */
+	asoc->c = new->c;
+	asoc->peer.rwnd = new->peer.rwnd;
+	asoc->peer.sack_needed = new->peer.sack_needed;
+	asoc->peer.i = new->peer.i;
+	sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_SIZE,
+			 asoc->peer.i.initial_tsn);
+
+	/* FIXME:
+	 *    Do we need to copy primary_path etc?
+	 *
+	 *    More explicitly, addresses may have been removed and
+	 *    this needs accounting for.
+	 */
+
+	/* If the case is A (association restart), use
+	 * initial_tsn as next_tsn. If the case is B, use
+	 * current next_tsn in case data sent to peer
+	 * has been discarded and needs retransmission.
+	 */
+	if (SCTP_STATE_ESTABLISHED == asoc->state) {
+
+		asoc->next_tsn = new->next_tsn;
+		asoc->ctsn_ack_point = new->ctsn_ack_point;
+
+		/* Reinitialize SSN for both local streams
+		 * and peer's streams.
+		 */
+		sctp_ssnmap_clear(asoc->ssnmap);
+
+	} else {
+		asoc->ctsn_ack_point = asoc->next_tsn - 1;
+		if (!asoc->ssnmap) {
+			/* Move the ssnmap. */
+			asoc->ssnmap = new->ssnmap;
+			new->ssnmap = NULL;
+		}
+	}
+
+}
+
+/* Update the retran path for sending a retransmitted packet.
+ * Round-robin through the active transports, else round-robin
+ * through the inactive transports as this is the next best thing
+ * we can try.
+ */
+void sctp_assoc_update_retran_path(struct sctp_association *asoc)
+{
+	struct sctp_transport *t, *next;
+	struct list_head *head = &asoc->peer.transport_addr_list;
+	struct list_head *pos;
+
+	/* Find the next transport in a round-robin fashion. */
+	t = asoc->peer.retran_path;
+	pos = &t->transports;
+	next = NULL;
+
+	while (1) {
+		/* Skip the head. */
+		if (pos->next == head)
+			pos = head->next;
+		else
+			pos = pos->next;
+
+		t = list_entry(pos, struct sctp_transport, transports);
+
+		/* Try to find an active transport. */
+
+		if (t->active) {
+			break;
+		} else {
+			/* Keep track of the next transport in case
+			 * we don't find any active transport.
+			 */
+			if (!next)
+				next = t;
+		}
+
+		/* We have exhausted the list, but didn't find any
+		 * other active transports.  If so, use the next
+		 * transport.
+		 */
+		if (t == asoc->peer.retran_path) {
+			t = next;
+			break;
+		}
+	}
+
+	asoc->peer.retran_path = t;
+}
+
+/* Choose the transport for sending a SHUTDOWN packet.  */
+struct sctp_transport *sctp_assoc_choose_shutdown_transport(struct sctp_association *asoc)
+{
+	/* If this is the first time SHUTDOWN is sent, use the active path,
+	 * else use the retran path. If the last SHUTDOWN was sent over the
+	 * retran path, update the retran path and use it.
+	 */
+	if (!asoc->shutdown_last_sent_to)
+		return asoc->peer.active_path;
+	else {
+		if (asoc->shutdown_last_sent_to == asoc->peer.retran_path)
+			sctp_assoc_update_retran_path(asoc);
+		return asoc->peer.retran_path;
+	}
+
+}
+
+/* Update the association's pmtu and frag_point by going through all the
+ * transports. This routine is called when a transport's PMTU has changed.
+ */
+void sctp_assoc_sync_pmtu(struct sctp_association *asoc)
+{
+	struct sctp_transport *t;
+	struct list_head *pos;
+	__u32 pmtu = 0;
+
+	if (!asoc)
+		return;
+
+	/* Get the lowest pmtu of all the transports. */
+	list_for_each(pos, &asoc->peer.transport_addr_list) {
+		t = list_entry(pos, struct sctp_transport, transports);
+		if (!pmtu || (t->pmtu < pmtu))
+			pmtu = t->pmtu;
+	}
+
+	if (pmtu) {
+		asoc->pmtu = pmtu;
+		asoc->frag_point = sctp_frag_point(pmtu);
+	}
+
+	SCTP_DEBUG_PRINTK("%s: asoc:%p, pmtu:%d, frag_point:%d\n",
+			  __FUNCTION__, asoc, asoc->pmtu, asoc->frag_point);
+}
+
+/* Should we send a SACK to update our peer? */
+static inline int sctp_peer_needs_update(struct sctp_association *asoc)
+{
+	switch (asoc->state) {
+	case SCTP_STATE_ESTABLISHED:
+	case SCTP_STATE_SHUTDOWN_PENDING:
+	case SCTP_STATE_SHUTDOWN_RECEIVED:
+		if ((asoc->rwnd > asoc->a_rwnd) &&
+		    ((asoc->rwnd - asoc->a_rwnd) >=
+		     min_t(__u32, (asoc->base.sk->rcvbuf >> 1), asoc->pmtu)))
+			return 1;
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+/* Increase asoc's rwnd by len and send any window update SACK if needed. */
+void sctp_assoc_rwnd_increase(struct sctp_association *asoc, int len)
+{
+	sctp_chunk_t *sack;
+	struct timer_list *timer;
+
+	if (asoc->rwnd_over) {
+		if (asoc->rwnd_over >= len) {
+			asoc->rwnd_over -= len;
+		} else {
+			asoc->rwnd += (len - asoc->rwnd_over);
+			asoc->rwnd_over = 0;
+		}
+	} else {
+		asoc->rwnd += len;
+	}
+
+	SCTP_DEBUG_PRINTK("%s: asoc %p rwnd increased by %d to (%u, %u) "
+			  "- %u\n", __FUNCTION__, asoc, len, asoc->rwnd,
+			  asoc->rwnd_over, asoc->a_rwnd);
+
+	/* Send a window update SACK if the rwnd has increased by at least the
+	 * minimum of the association's PMTU and half of the receive buffer.
+	 * The algorithm used is similar to the one described in
+	 * Section 4.2.3.3 of RFC 1122.
+	 */
+	if (sctp_peer_needs_update(asoc)) {
+		asoc->a_rwnd = asoc->rwnd;
+		SCTP_DEBUG_PRINTK("%s: Sending window update SACK- asoc: %p "
+				  "rwnd: %u a_rwnd: %u\n", __FUNCTION__,
+				  asoc, asoc->rwnd, asoc->a_rwnd);
+		sack = sctp_make_sack(asoc);
+		if (!sack)
+			return;
+
+		asoc->peer.sack_needed = 0;
+
+		sctp_outq_tail(&asoc->outqueue, sack);
+
+		/* Stop the SACK timer.  */
+		timer = &asoc->timers[SCTP_EVENT_TIMEOUT_SACK];
+		if (timer_pending(timer) && del_timer(timer))
+			sctp_association_put(asoc);
+	}
+}
+
+/* Decrease asoc's rwnd by len. */
+void sctp_assoc_rwnd_decrease(struct sctp_association *asoc, int len)
+{
+	SCTP_ASSERT(asoc->rwnd, "rwnd zero", return);
+	SCTP_ASSERT(!asoc->rwnd_over, "rwnd_over not zero", return);
+	if (asoc->rwnd >= len) {
+		asoc->rwnd -= len;
+	} else {
+		asoc->rwnd_over = len - asoc->rwnd;
+		asoc->rwnd = 0;
+	}
+	SCTP_DEBUG_PRINTK("%s: asoc %p rwnd decreased by %d to (%u, %u)\n",
+			  __FUNCTION__, asoc, len, asoc->rwnd,
+			  asoc->rwnd_over);
+}
+
+/* Build the bind address list for the association based on info from the
+ * local endpoint and the remote peer.
+ */
+int sctp_assoc_set_bind_addr_from_ep(struct sctp_association *asoc, int gfp)
+{
+	sctp_scope_t scope;
+	int flags;
+
+	/* Use scoping rules to determine the subset of addresses from
+	 * the endpoint.
+	 */
+	scope = sctp_scope(&asoc->peer.active_path->ipaddr);
+	flags = (PF_INET6 == asoc->base.sk->family) ? SCTP_ADDR6_ALLOWED : 0;
+	if (asoc->peer.ipv4_address)
+		flags |= SCTP_ADDR4_PEERSUPP;
+	if (asoc->peer.ipv6_address)
+		flags |= SCTP_ADDR6_PEERSUPP;
+
+	return sctp_bind_addr_copy(&asoc->base.bind_addr,
+				   &asoc->ep->base.bind_addr,
+				   scope, gfp, flags);
+}
+
+/* Build the association's bind address list from the cookie.  */
+int sctp_assoc_set_bind_addr_from_cookie(struct sctp_association *asoc,
+					 sctp_cookie_t *cookie, int gfp)
+{
+	int var_size2 = ntohs(cookie->peer_init->chunk_hdr.length);
+	int var_size3 = cookie->raw_addr_list_len;
+	__u8 *raw = (__u8 *)cookie + sizeof(sctp_cookie_t) + var_size2;
+
+	return sctp_raw_to_bind_addrs(&asoc->base.bind_addr, raw, var_size3,
+				      asoc->ep->base.bind_addr.port, gfp);
+}
diff -urN linux-2.4.23-pre5/net/sctp/bind_addr.c linux-2.4.23-pre6/net/sctp/bind_addr.c
--- linux-2.4.23-pre5/net/sctp/bind_addr.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/bind_addr.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,396 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) Cisco 1999,2000
+ * Copyright (c) Motorola 1999,2000,2001
+ * Copyright (c) International Business Machines Corp., 2001,2002
+ * Copyright (c) La Monte H.P. Yarroll 2001
+ *
+ * This file is part of the SCTP kernel reference implementation.
+ *
+ * A collection class to handle the storage of transport addresses.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Karl Knutson          <karl@athena.chicago.il.us>
+ *    Jon Grimm             <jgrimm@us.ibm.com>
+ *    Daisy Chang           <daisyc@us.ibm.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/in.h>
+#include <net/sock.h>
+#include <net/ipv6.h>
+#include <net/if_inet6.h>
+#include <net/sctp/sctp.h>
+#include <net/sctp/sm.h>
+
+/* Forward declarations for internal helpers. */
+static int sctp_copy_one_addr(sctp_bind_addr_t *, union sctp_addr *,
+			      sctp_scope_t scope, int gfp, int flags);
+static void sctp_bind_addr_clean(sctp_bind_addr_t *);
+
+/* First Level Abstractions. */
+
+/* Copy 'src' to 'dest' taking 'scope' into account.  Omit addresses
+ * in 'src' which have a broader scope than 'scope'.
+ */
+int sctp_bind_addr_copy(sctp_bind_addr_t *dest, const sctp_bind_addr_t *src,
+			sctp_scope_t scope, int gfp, int flags)
+{
+	struct sockaddr_storage_list *addr;
+	struct list_head *pos;
+	int error = 0;
+
+	/* All addresses share the same port.  */
+	dest->port = src->port;
+
+	/* Extract the addresses which are relevant for this scope.  */
+	list_for_each(pos, &src->address_list) {
+		addr = list_entry(pos, struct sockaddr_storage_list, list);
+		error = sctp_copy_one_addr(dest, &addr->a, scope,
+					   gfp, flags);
+		if (error < 0)
+			goto out;
+	}
+
+	/* If there are no addresses matching the scope and
+	 * this is global scope, try to get a link scope address, with
+	 * the assumption that we must be sitting behind a NAT.
+	 */
+	if (list_empty(&dest->address_list) && (SCTP_SCOPE_GLOBAL == scope)) {
+		list_for_each(pos, &src->address_list) {
+			addr = list_entry(pos, struct sockaddr_storage_list,
+					  list);
+			error = sctp_copy_one_addr(dest, &addr->a,
+						   SCTP_SCOPE_LINK, gfp,
+						   flags);
+			if (error < 0)
+				goto out;
+		}
+	}
+
+out:
+	if (error)
+		sctp_bind_addr_clean(dest);
+
+	return error;
+}
+
+/* Create a new SCTP_bind_addr from nothing.  */
+sctp_bind_addr_t *sctp_bind_addr_new(int gfp)
+{
+	sctp_bind_addr_t *retval;
+
+	retval = t_new(sctp_bind_addr_t, gfp);
+	if (!retval)
+		goto nomem;
+
+	sctp_bind_addr_init(retval, 0);
+	retval->malloced = 1;
+	SCTP_DBG_OBJCNT_INC(bind_addr);
+
+nomem:
+	return retval;
+}
+
+/* Initialize the SCTP_bind_addr structure for either an endpoint or
+ * an association.
+ */
+void sctp_bind_addr_init(sctp_bind_addr_t *bp, __u16 port)
+{
+	bp->malloced = 0;
+
+	INIT_LIST_HEAD(&bp->address_list);
+	bp->port = port;
+}
+
+/* Dispose of the address list. */
+static void sctp_bind_addr_clean(sctp_bind_addr_t *bp)
+{
+	struct sockaddr_storage_list *addr;
+	struct list_head *pos, *temp;
+
+	/* Empty the bind address list. */
+	list_for_each_safe(pos, temp, &bp->address_list) {
+		addr = list_entry(pos, struct sockaddr_storage_list, list);
+		list_del(pos);
+		kfree(addr);
+		SCTP_DBG_OBJCNT_DEC(addr);
+	}
+}
+
+/* Dispose of an SCTP_bind_addr structure  */
+void sctp_bind_addr_free(sctp_bind_addr_t *bp)
+{
+	/* Empty the bind address list. */
+	sctp_bind_addr_clean(bp);
+
+	if (bp->malloced) {
+		kfree(bp);
+		SCTP_DBG_OBJCNT_DEC(bind_addr);
+	}
+}
+
+/* Add an address to the bind address list in the SCTP_bind_addr structure. */
+int sctp_add_bind_addr(sctp_bind_addr_t *bp, union sctp_addr *new,
+		       int gfp)
+{
+	struct sockaddr_storage_list *addr;
+
+	/* Add the address to the bind address list.  */
+	addr = t_new(struct sockaddr_storage_list, gfp);
+	if (!addr)
+		return -ENOMEM;
+
+	addr->a = *new;
+
+	/* Fix up the port if it has not yet been set.
+	 * Both v4 and v6 have the port at the same offset.
+	 */
+	if (!addr->a.v4.sin_port)
+		addr->a.v4.sin_port = bp->port;
+
+	INIT_LIST_HEAD(&addr->list);
+	list_add_tail(&addr->list, &bp->address_list);
+	SCTP_DBG_OBJCNT_INC(addr);
+
+	return 0;
+}
+
+/* Delete an address from the bind address list in the SCTP_bind_addr
+ * structure.
+ */
+int sctp_del_bind_addr(sctp_bind_addr_t *bp, union sctp_addr *del_addr)
+{
+	struct list_head *pos, *temp;
+	struct sockaddr_storage_list *addr;
+
+	list_for_each_safe(pos, temp, &bp->address_list) {
+		addr = list_entry(pos, struct sockaddr_storage_list, list);
+		if (sctp_cmp_addr_exact(&addr->a, del_addr)) {
+			/* Found the exact match. */
+			list_del(pos);
+			kfree(addr);
+			SCTP_DBG_OBJCNT_DEC(addr);
+
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+/* Create a network byte-order representation of all the addresses
+ * formated as SCTP parameters.
+ *
+ * The second argument is the return value for the length.
+ */
+union sctp_params sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp,
+					 int *addrs_len, int gfp)
+{
+	union sctp_params addrparms;
+	union sctp_params retval;
+	int addrparms_len;
+	sctp_addr_param_t rawaddr;
+	int len;
+	struct sockaddr_storage_list *addr;
+	struct list_head *pos;
+	addrparms_len = 0;
+	len = 0;
+
+	/* Allocate enough memory at once. */
+	list_for_each(pos, &bp->address_list) {
+		len += sizeof(sctp_addr_param_t);
+	}
+
+	/* Don't even bother embedding an address if there
+	 * is only one.
+	 */
+	if (len == sizeof(sctp_addr_param_t)) {
+		retval.v = NULL;
+		goto end_raw;
+	}
+
+	retval.v = kmalloc(len, gfp);
+	if (!retval.v)
+		goto end_raw;
+
+	addrparms = retval;
+
+	list_for_each(pos, &bp->address_list) {
+		addr = list_entry(pos, struct sockaddr_storage_list, list);
+		len = sockaddr2sctp_addr(&addr->a, &rawaddr);
+		memcpy(addrparms.v, &rawaddr, len);
+		addrparms.v += len;
+		addrparms_len += len;
+	}
+
+end_raw:
+	*addrs_len = addrparms_len;
+	return retval;
+}
+
+/*
+ * Create an address list out of the raw address list format (IPv4 and IPv6
+ * address parameters).
+ */
+int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp, __u8 *raw_addr_list,
+			   int addrs_len, __u16 port, int gfp)
+{
+	sctp_addr_param_t *rawaddr;
+	sctp_paramhdr_t *param;
+	union sctp_addr addr;
+	int retval = 0;
+	int len;
+
+	/* Convert the raw address to standard address format */
+	while (addrs_len) {
+		param = (sctp_paramhdr_t *)raw_addr_list;
+		rawaddr = (sctp_addr_param_t *)raw_addr_list;
+
+		switch (param->type) {
+		case SCTP_PARAM_IPV4_ADDRESS:
+		case SCTP_PARAM_IPV6_ADDRESS:
+			sctp_param2sockaddr(&addr, rawaddr, port, 0);
+			retval = sctp_add_bind_addr(bp, &addr, gfp);
+			if (retval) {
+				/* Can't finish building the list, clean up. */
+				sctp_bind_addr_clean(bp);
+				break;;
+			}
+			len = ntohs(param->length);
+			addrs_len -= len;
+			raw_addr_list += len;
+			break;
+		default:
+			/* Corrupted raw addr list! */
+			retval = -EINVAL;
+			sctp_bind_addr_clean(bp);
+			break;
+		}
+		if (retval)
+			break;
+	}
+
+	return retval;
+}
+
+/********************************************************************
+ * 2nd Level Abstractions
+ ********************************************************************/
+
+/* Does this contain a specified address?  Allow wildcarding. */
+int sctp_bind_addr_match(sctp_bind_addr_t *bp, const union sctp_addr *addr,
+			 struct sctp_opt *opt)
+{
+	struct sockaddr_storage_list *laddr;
+	struct list_head *pos;
+
+	list_for_each(pos, &bp->address_list) {
+		laddr = list_entry(pos, struct sockaddr_storage_list, list);
+		if (opt->pf->cmp_addr(&laddr->a, addr, opt))
+ 			return 1;
+	}
+
+	return 0;
+}
+
+/* Copy out addresses from the global local address list. */
+static int sctp_copy_one_addr(sctp_bind_addr_t *dest, union sctp_addr *addr,
+			      sctp_scope_t scope, int gfp, int flags)
+{
+	struct sctp_protocol *proto = sctp_get_protocol();
+	int error = 0;
+
+	if (sctp_is_any(addr)) {
+		error = sctp_copy_local_addr_list(proto, dest, scope,
+						  gfp, flags);
+	} else if (sctp_in_scope(addr, scope)) {
+		/* Now that the address is in scope, check to see if
+		 * the address type is supported by local sock as
+		 * well as the remote peer.
+		 */
+		if ((((AF_INET == addr->sa.sa_family) &&
+		      (flags & SCTP_ADDR4_PEERSUPP))) ||
+		    (((AF_INET6 == addr->sa.sa_family) &&
+		      (flags & SCTP_ADDR6_ALLOWED) &&
+		      (flags & SCTP_ADDR6_PEERSUPP))))
+			error = sctp_add_bind_addr(dest, addr, gfp);
+	}
+
+	return error;
+}
+
+/* Is this a wildcard address?  */
+int sctp_is_any(const union sctp_addr *addr)
+{
+	struct sctp_af *af = sctp_get_af_specific(addr->sa.sa_family);
+	if (!af)
+		return 0;
+	return af->is_any(addr);
+}
+
+/* Is 'addr' valid for 'scope'?  */
+int sctp_in_scope(const union sctp_addr *addr, sctp_scope_t scope)
+{
+	sctp_scope_t addr_scope = sctp_scope(addr);
+
+	/* The unusable SCTP addresses will not be considered with
+	 * any defined scopes.
+	 */
+	if (SCTP_SCOPE_UNUSABLE == addr_scope)
+		return 0;
+	/*
+	 * For INIT and INIT-ACK address list, let L be the level of
+	 * of requested destination address, sender and receiver
+	 * SHOULD include all of its addresses with level greater
+	 * than or equal to L.
+	 */
+	if (addr_scope <= scope)
+		return 1;
+
+	return 0;
+}
+
+/********************************************************************
+ * 3rd Level Abstractions
+ ********************************************************************/
+
+/* What is the scope of 'addr'?  */
+sctp_scope_t sctp_scope(const union sctp_addr *addr)
+{
+	struct sctp_af *af;
+
+	af = sctp_get_af_specific(addr->sa.sa_family);
+	if (!af)
+		return SCTP_SCOPE_UNUSABLE;
+
+	return af->scope((union sctp_addr *)addr);
+}
diff -urN linux-2.4.23-pre5/net/sctp/command.c linux-2.4.23-pre6/net/sctp/command.c
--- linux-2.4.23-pre5/net/sctp/command.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/command.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,104 @@
+/* SCTP kernel reference Implementation Copyright (C) 1999-2001
+ * Cisco, Motorola, and IBM
+ * Copyright 2001 La Monte H.P. Yarroll
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * These functions manipulate sctp command sequences.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Karl Knutson <karl@athena.chicago.il.us>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/types.h>
+#include <net/sctp/sctp.h>
+#include <net/sctp/sm.h>
+
+/* Create a new sctp_command_sequence.  */
+sctp_cmd_seq_t *sctp_new_cmd_seq(int gfp)
+{
+	sctp_cmd_seq_t *retval = t_new(sctp_cmd_seq_t, gfp);
+
+	if (retval)
+		sctp_init_cmd_seq(retval);
+
+	return retval;
+}
+
+/* Initialize a block of memory as a command sequence. */
+int sctp_init_cmd_seq(sctp_cmd_seq_t *seq)
+{
+	memset(seq, 0, sizeof(sctp_cmd_seq_t));
+	return 1;		/* We always succeed.  */
+}
+
+/* Add a command to a sctp_cmd_seq_t.
+ * Return 0 if the command sequence is full.
+ */
+int sctp_add_cmd(sctp_cmd_seq_t *seq, sctp_verb_t verb, sctp_arg_t obj)
+{
+	if (seq->next_free_slot >= SCTP_MAX_NUM_COMMANDS)
+		goto fail;
+
+	seq->cmds[seq->next_free_slot].verb = verb;
+	seq->cmds[seq->next_free_slot++].obj = obj;
+
+	return 1;
+
+fail:
+	return 0;
+}
+
+/* Rewind an sctp_cmd_seq_t to iterate from the start.  */
+int sctp_rewind_sequence(sctp_cmd_seq_t *seq)
+{
+	seq->next_cmd = 0;
+	return 1;		/* We always succeed. */
+}
+
+/* Return the next command structure in a sctp_cmd_seq.
+ * Returns NULL at the end of the sequence.
+ */
+sctp_cmd_t *sctp_next_cmd(sctp_cmd_seq_t *seq)
+{
+	sctp_cmd_t *retval = NULL;
+
+	if (seq->next_cmd < seq->next_free_slot)
+		retval = &seq->cmds[seq->next_cmd++];
+
+	return retval;
+}
+
+/* Dispose of a command sequence.  */
+void sctp_free_cmd_seq(sctp_cmd_seq_t *seq)
+{
+	kfree(seq);
+}
diff -urN linux-2.4.23-pre5/net/sctp/crc32c.c linux-2.4.23-pre6/net/sctp/crc32c.c
--- linux-2.4.23-pre5/net/sctp/crc32c.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/crc32c.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,220 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001-2003 International Business Machines, Corp.
+ * 
+ * This file is part of the SCTP kernel reference Implementation
+ * 
+ * SCTP Checksum functions
+ * 
+ * The SCTP reference implementation is free software; 
+ * you can redistribute it and/or modify it under the terms of 
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ * 
+ * The SCTP reference implementation is distributed in the hope that it 
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.  
+ * 
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ * 
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by: 
+ *    Dinakaran Joseph 
+ *    Jon Grimm <jgrimm@us.ibm.com>
+ *    Sridhar Samudrala <sri@us.ibm.com>
+ * 
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+/* The following code has been taken directly from
+ * draft-ietf-tsvwg-sctpcsum-03.txt
+ *
+ * The code has now been modified specifically for SCTP knowledge.
+ */
+
+#include <linux/types.h>
+#include <net/sctp/sctp.h>
+
+#define CRC32C_POLY 0x1EDC6F41
+#define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF])
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/* Copyright 2001, D. Otis.  Use this program, code or tables    */
+/* extracted from it, as desired without restriction.            */
+/*                                                               */
+/* 32 Bit Reflected CRC table generation for SCTP.               */
+/* To accommodate serial byte data being shifted out least       */
+/* significant bit first, the table's 32 bit words are reflected */
+/* which flips both byte and bit MS and LS positions.  The CRC   */
+/* is calculated MS bits first from the perspective of the serial*/
+/* stream.  The x^32 term is implied and the x^0 term may also   */
+/* be shown as +1.  The polynomial code used is 0x1EDC6F41.      */
+/* Castagnoli93                                                  */
+/* x^32+x^28+x^27+x^26+x^25+x^23+x^22+x^20+x^19+x^18+x^14+x^13+  */
+/* x^11+x^10+x^9+x^8+x^6+x^0                                     */
+/* Guy Castagnoli Stefan Braeuer and Martin Herrman              */
+/* "Optimization of Cyclic Redundancy-Check Codes                */
+/* with 24 and 32 Parity Bits",                                  */
+/* IEEE Transactions on Communications, Vol.41, No.6, June 1993  */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+__u32 crc_c[256] = {
+	0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
+	0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
+	0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
+	0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
+	0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
+	0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
+	0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54,
+	0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
+	0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
+	0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
+	0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5,
+	0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
+	0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45,
+	0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
+	0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
+	0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
+	0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48,
+	0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
+	0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687,
+	0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
+	0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
+	0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
+	0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8,
+	0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
+	0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
+	0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
+	0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
+	0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
+	0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9,
+	0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
+	0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36,
+	0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
+	0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
+	0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
+	0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
+	0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
+	0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3,
+	0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
+	0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
+	0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
+	0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652,
+	0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
+	0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D,
+	0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
+	0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
+	0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
+	0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2,
+	0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
+	0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530,
+	0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
+	0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
+	0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
+	0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F,
+	0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
+	0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
+	0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
+	0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
+	0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
+	0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321,
+	0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
+	0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81,
+	0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
+	0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
+	0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351,
+};
+     
+__u32 sctp_start_cksum(__u8 *buffer, __u16 length)
+{
+    	__u32 crc32 = ~(__u32) 0;
+	__u32 i;
+
+	/* Optimize this routine to be SCTP specific, knowing how
+	 * to skip the checksum field of the SCTP header.
+	 */
+
+	/* Calculate CRC up to the checksum. */
+	for (i = 0; i < (sizeof(struct sctphdr) - sizeof(__u32)); i++)
+      		CRC32C(crc32, buffer[i]);
+
+	/* Skip checksum field of the header. */
+	for (i = 0; i < sizeof(__u32); i++)
+		CRC32C(crc32, 0);
+
+	/* Calculate the rest of the CRC. */
+	for (i = sizeof(struct sctphdr); i < length ; i++)
+		CRC32C(crc32, buffer[i]);
+
+	return crc32;
+}
+
+__u32 sctp_update_cksum(__u8 *buffer, __u16 length, __u32 crc32)
+{
+	__u32 i;
+
+	for (i = 0; i < length ; i++)
+		CRC32C(crc32, buffer[i]);
+
+	return crc32;
+}
+
+__u32 sctp_update_copy_cksum(__u8 *to, __u8 *from, __u16 length, __u32 crc32)
+{
+	__u32 i;
+	__u32 *_to = (__u32 *)to;
+	__u32 *_from = (__u32 *)from;
+	
+	for (i = 0; i < (length/4); i++) {
+		_to[i] = _from[i];
+		CRC32C(crc32, from[i*4]);
+		CRC32C(crc32, from[i*4+1]);
+		CRC32C(crc32, from[i*4+2]);
+		CRC32C(crc32, from[i*4+3]);	
+	}
+
+	return crc32;
+}
+
+__u32 sctp_end_cksum(__u32 crc32)
+{
+	__u32 result;
+	__u8 byte0, byte1, byte2, byte3;
+
+	result = ~crc32;
+
+	/*  result  now holds the negated polynomial remainder;
+	 *  since the table and algorithm is "reflected" [williams95].
+	 *  That is,  result has the same value as if we mapped the message
+	 *  to a polyomial, computed the host-bit-order polynomial
+	 *  remainder, performed final negation, then did an end-for-end
+	 *  bit-reversal.
+	 *  Note that a 32-bit bit-reversal is identical to four inplace
+	 *  8-bit reversals followed by an end-for-end byteswap.
+	 *  In other words, the bytes of each bit are in the right order,
+	 *  but the bytes have been byteswapped.  So we now do an explicit
+	 *  byteswap.  On a little-endian machine, this byteswap and
+	 *  the final ntohl cancel out and could be elided.
+	 */
+	byte0 = result & 0xff;
+	byte1 = (result>>8) & 0xff;
+	byte2 = (result>>16) & 0xff;
+	byte3 = (result>>24) & 0xff;
+
+	crc32 = ((byte0 << 24) |
+		 (byte1 << 16) |
+		 (byte2 << 8)  |
+		 byte3);
+	return crc32;
+}
diff -urN linux-2.4.23-pre5/net/sctp/debug.c linux-2.4.23-pre6/net/sctp/debug.c
--- linux-2.4.23-pre5/net/sctp/debug.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/debug.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,202 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001 Intel Corp.
+ * Copyright (c) 2001 International Business Machines Corp.
+ * 
+ * This file is part of the SCTP kernel reference Implementation
+ * 
+ * This file is part of the implementation of the add-IP extension,
+ * based on <draft-ietf-tsvwg-addip-sctp-02.txt> June 29, 2001,
+ * for the SCTP kernel reference Implementation.
+ * 
+ * This file converts numerical ID value to alphabetical names for SCTP
+ * terms such as chunk type, parameter time, event type, etc.
+ * 
+ * The SCTP reference implementation is free software; 
+ * you can redistribute it and/or modify it under the terms of 
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ * 
+ * The SCTP reference implementation is distributed in the hope that it 
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.  
+ * 
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ * 
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by: 
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Karl Knutson          <karl@athena.chicago.il.us>
+ *    Xingang Guo           <xingang.guo@intel.com>
+ *    Jon Grimm             <jgrimm@us.ibm.com>
+ *    Daisy Chang	    <daisyc@us.ibm.com>
+ * 
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <net/sctp/sctp.h>
+
+#if SCTP_DEBUG
+int sctp_debug_flag = 1;	/* Initially enable DEBUG */
+#endif	/* SCTP_DEBUG */
+
+/* These are printable forms of Chunk ID's from section 3.1.  */
+static const char *sctp_cid_tbl[SCTP_NUM_BASE_CHUNK_TYPES] = {
+	"DATA",
+	"INIT",
+	"INIT_ACK",
+	"SACK",
+	"HEARTBEAT",
+	"HEARTBEAT_ACK",
+	"ABORT",
+	"SHUTDOWN",
+	"SHUTDOWN_ACK",
+	"ERROR",
+	"COOKIE_ECHO",
+	"COOKIE_ACK",
+	"ECN_ECNE",
+	"ECN_CWR",
+	"SHUTDOWN_COMPLETE",
+};
+
+/* Lookup "chunk type" debug name. */
+const char *sctp_cname(const sctp_subtype_t cid)
+{
+	if (cid.chunk < 0)
+		return "illegal chunk id";
+	if (cid.chunk <= SCTP_CID_BASE_MAX)
+		return sctp_cid_tbl[cid.chunk];
+	
+	switch (cid.chunk) {
+	case SCTP_CID_ASCONF:
+		return "ASCONF";
+
+	case SCTP_CID_ASCONF_ACK:
+		return "ASCONF_ACK";
+
+	default:
+		return "unknown chunk";
+	};
+	return "unknown chunk";
+}
+
+/* These are printable form of variable-length parameters. */
+const char *sctp_param_tbl[SCTP_PARAM_ECN_CAPABLE + 1] = {
+	"",
+	"PARAM_HEARTBEAT_INFO",
+	"",
+	"",
+	"",
+	"PARAM_IPV4_ADDRESS",
+	"PARAM_IPV6_ADDRESS",
+	"PARAM_STATE_COOKIE",
+	"PARAM_UNRECOGNIZED_PARAMETERS",
+	"PARAM_COOKIE_PRESERVATIVE",
+	"",
+	"PARAM_HOST_NAME_ADDRESS",
+	"PARAM_SUPPORTED_ADDRESS_TYPES",
+};
+
+/* These are printable forms of the states.  */
+const char *sctp_state_tbl[SCTP_STATE_NUM_STATES] = {
+	"STATE_EMPTY",
+	"STATE_CLOSED",
+	"STATE_COOKIE_WAIT",
+	"STATE_COOKIE_ECHOED",
+	"STATE_ESTABLISHED",
+	"STATE_SHUTDOWN_PENDING",
+	"STATE_SHUTDOWN_SENT",
+	"STATE_SHUTDOWN_RECEIVED",
+	"STATE_SHUTDOWN_ACK_SENT",
+};
+
+/* Events that could change the state of an association.  */
+const char *sctp_evttype_tbl[] = {
+	"EVENT_T_unknown",
+	"EVENT_T_CHUNK",
+	"EVENT_T_TIMEOUT",
+	"EVENT_T_OTHER",
+	"EVENT_T_PRIMITIVE"
+};
+
+/* Return value of a state function */
+const char *sctp_status_tbl[] = {
+	"DISPOSITION_DISCARD",
+	"DISPOSITION_CONSUME",
+	"DISPOSITION_NOMEM",
+	"DISPOSITION_DELETE_TCB",
+	"DISPOSITION_ABORT",
+	"DISPOSITION_VIOLATION",
+	"DISPOSITION_NOT_IMPL",
+	"DISPOSITION_ERROR",
+	"DISPOSITION_BUG"
+};
+
+/* Printable forms of primitives */
+static const char *sctp_primitive_tbl[SCTP_NUM_PRIMITIVE_TYPES] = {
+	"PRIMITIVE_ASSOCIATE",
+	"PRIMITIVE_SHUTDOWN",
+	"PRIMITIVE_ABORT",
+	"PRIMITIVE_SEND",
+	"PRIMITIVE_REQUESTHEARTBEAT",
+};
+
+/* Lookup primitive debug name. */
+const char *sctp_pname(const sctp_subtype_t id)
+{
+	if (id.primitive < 0)
+		return "illegal primitive";
+	if (id.primitive <= SCTP_EVENT_PRIMITIVE_MAX)
+		return sctp_primitive_tbl[id.primitive];
+	return "unknown_primitive";
+}
+
+static const char *sctp_other_tbl[] = {
+	"NO_PENDING_TSN",
+};
+
+/* Lookup "other" debug name. */
+const char *sctp_oname(const sctp_subtype_t id)
+{
+	if (id.other < 0)
+		return "illegal 'other' event";
+	if (id.other < SCTP_EVENT_OTHER_MAX)
+		return sctp_other_tbl[id.other];
+	return "unknown 'other' event";
+}
+
+static const char *sctp_timer_tbl[] = {
+	"TIMEOUT_NONE",
+	"TIMEOUT_T1_COOKIE",
+	"TIMEOUT_T1_INIT",
+	"TIMEOUT_T2_SHUTDOWN",
+	"TIMEOUT_T3_RTX",
+	"TIMEOUT_T5_SHUTDOWN_GUARD",
+	"TIMEOUT_HEARTBEAT",
+	"TIMEOUT_SACK",
+	"TIMEOUT_AUTOCLOSE",
+};
+
+/* Lookup timer debug name. */
+const char *sctp_tname(const sctp_subtype_t id)
+{
+	if (id.timeout < 0)
+		return "illegal 'timer' event";
+	if (id.timeout <= SCTP_EVENT_TIMEOUT_MAX)
+		return sctp_timer_tbl[id.timeout];
+	return "unknown_timer";
+}
diff -urN linux-2.4.23-pre5/net/sctp/endpointola.c linux-2.4.23-pre6/net/sctp/endpointola.c
--- linux-2.4.23-pre5/net/sctp/endpointola.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/endpointola.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,396 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001-2002 International Business Machines, Corp.
+ * Copyright (c) 2001 Intel Corp.
+ * Copyright (c) 2001 Nokia, Inc.
+ * Copyright (c) 2001 La Monte H.P. Yarroll
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * This abstraction represents an SCTP endpoint.
+ *
+ * This file is part of the implementation of the add-IP extension,
+ * based on <draft-ietf-tsvwg-addip-sctp-02.txt> June 29, 2001,
+ * for the SCTP kernel reference Implementation.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Karl Knutson <karl@athena.chicago.il.us>
+ *    Jon Grimm <jgrimm@austin.ibm.com>
+ *    Daisy Chang <daisyc@us.ibm.com>
+ *    Dajiang Zhang <dajiang.zhang@nokia.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/in.h>
+#include <linux/random.h>	/* get_random_bytes() */
+#include <net/sock.h>
+#include <net/ipv6.h>
+#include <net/sctp/sctp.h>
+#include <net/sctp/sm.h>
+
+/* Forward declarations for internal helpers. */
+static void sctp_endpoint_bh_rcv(struct sctp_endpoint *ep);
+
+/* Create a sctp_endpoint with all that boring stuff initialized.
+ * Returns NULL if there isn't enough memory.
+ */
+struct sctp_endpoint *sctp_endpoint_new(struct sock *sk, int gfp)
+{
+	struct sctp_endpoint *ep;
+
+	/* Build a local endpoint. */
+	ep = t_new(struct sctp_endpoint, gfp);
+	if (!ep)
+		goto fail;
+	if (!sctp_endpoint_init(ep, sk, gfp))
+		goto fail_init;
+	ep->base.malloced = 1;
+	SCTP_DBG_OBJCNT_INC(ep);
+	return ep;
+
+fail_init:
+	kfree(ep);
+fail:
+	return NULL;
+}
+
+/*
+ * Initialize the base fields of the endpoint structure.
+ */
+struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep,
+					 struct sock *sk, int gfp)
+{
+	struct sctp_opt *sp = sctp_sk(sk);
+	memset(ep, 0, sizeof(struct sctp_endpoint));
+
+	/* Initialize the base structure. */
+	/* What type of endpoint are we?  */
+	ep->base.type = SCTP_EP_TYPE_SOCKET;
+
+	/* Initialize the basic object fields. */
+	atomic_set(&ep->base.refcnt, 1);
+	ep->base.dead = 0;
+	ep->base.malloced = 1;
+
+	/* Create an input queue.  */
+	sctp_inq_init(&ep->base.inqueue);
+
+	/* Set its top-half handler */
+	sctp_inq_set_th_handler(&ep->base.inqueue,
+				(void (*)(void *))sctp_endpoint_bh_rcv, ep);
+
+	/* Initialize the bind addr area */
+	sctp_bind_addr_init(&ep->base.bind_addr, 0);
+	ep->base.addr_lock = RW_LOCK_UNLOCKED;
+
+	/* Remember who we are attached to.  */
+	ep->base.sk = sk;
+	sock_hold(ep->base.sk);
+
+	/* Create the lists of associations.  */
+	INIT_LIST_HEAD(&ep->asocs);
+
+	/* Set up the base timeout information.  */
+	ep->timeouts[SCTP_EVENT_TIMEOUT_NONE] = 0;
+	ep->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE] =
+		SCTP_DEFAULT_TIMEOUT_T1_COOKIE;
+	ep->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT] =
+		SCTP_DEFAULT_TIMEOUT_T1_INIT;
+	ep->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] =
+		sp->rtoinfo.srto_initial;
+	ep->timeouts[SCTP_EVENT_TIMEOUT_T3_RTX] = 0;
+
+	/* sctpimpguide-05 Section 2.12.2
+	 * If the 'T5-shutdown-guard' timer is used, it SHOULD be set to the
+	 * recommended value of 5 times 'RTO.Max'.
+	 */
+        ep->timeouts[SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD]
+		= 5 * sp->rtoinfo.srto_max;
+
+	ep->timeouts[SCTP_EVENT_TIMEOUT_HEARTBEAT] =
+		SCTP_DEFAULT_TIMEOUT_HEARTBEAT;
+	ep->timeouts[SCTP_EVENT_TIMEOUT_SACK] =
+		SCTP_DEFAULT_TIMEOUT_SACK;
+	ep->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE] =
+		sp->autoclose * HZ;
+
+	/* Set up the default send/receive buffer space.  */
+
+	/* FIXME - Should the min and max window size be configurable
+	 * sysctl parameters as opposed to be constants?
+	 */
+	sk->rcvbuf = SCTP_DEFAULT_MAXWINDOW;
+	sk->sndbuf = SCTP_DEFAULT_MAXWINDOW * 2;
+
+	/* Use SCTP specific send buffer space queues.  */
+	sk->write_space = sctp_write_space;
+	sk->use_write_queue = 1;
+
+	/* Initialize the secret key used with cookie. */
+	get_random_bytes(&ep->secret_key[0], SCTP_SECRET_SIZE);
+	ep->last_key = ep->current_key = 0;
+	ep->key_changed_at = jiffies;
+
+	ep->debug_name = "unnamedEndpoint";
+	return ep;
+}
+
+/* Add an association to an endpoint.  */
+void sctp_endpoint_add_asoc(struct sctp_endpoint *ep,
+			    struct sctp_association *asoc)
+{
+	struct sock *sk = ep->base.sk;
+
+	/* Now just add it to our list of asocs */
+	list_add_tail(&asoc->asocs, &ep->asocs);
+
+	/* Increment the backlog value for a TCP-style listening socket. */
+	if ((SCTP_SOCKET_TCP == sctp_sk(sk)->type) &&
+	    (SCTP_SS_LISTENING == sk->state))
+		sk->ack_backlog++;
+}
+
+/* Free the endpoint structure.  Delay cleanup until
+ * all users have released their reference count on this structure.
+ */
+void sctp_endpoint_free(struct sctp_endpoint *ep)
+{
+	ep->base.dead = 1;
+	sctp_endpoint_put(ep);
+}
+
+/* Final destructor for endpoint.  */
+void sctp_endpoint_destroy(struct sctp_endpoint *ep)
+{
+	SCTP_ASSERT(ep->base.dead, "Endpoint is not dead", return);
+
+	ep->base.sk->state = SCTP_SS_CLOSED;
+
+	/* Unlink this endpoint, so we can't find it again! */
+	sctp_unhash_endpoint(ep);
+
+	/* Free up the HMAC transform. */
+	if (sctp_sk(ep->base.sk)->hmac)
+		sctp_crypto_free_tfm(sctp_sk(ep->base.sk)->hmac);
+
+	/* Cleanup. */
+	sctp_inq_free(&ep->base.inqueue);
+	sctp_bind_addr_free(&ep->base.bind_addr);
+
+	/* Remove and free the port */
+	if (ep->base.sk->prev != NULL)
+		sctp_put_port(ep->base.sk);
+
+	/* Give up our hold on the sock. */
+	if (ep->base.sk)
+		sock_put(ep->base.sk);
+
+	/* Finally, free up our memory. */
+	if (ep->base.malloced) {
+		kfree(ep);
+		SCTP_DBG_OBJCNT_DEC(ep);
+	}
+}
+
+/* Hold a reference to an endpoint. */
+void sctp_endpoint_hold(struct sctp_endpoint *ep)
+{
+	atomic_inc(&ep->base.refcnt);
+}
+
+/* Release a reference to an endpoint and clean up if there are
+ * no more references.
+ */
+void sctp_endpoint_put(struct sctp_endpoint *ep)
+{
+	if (atomic_dec_and_test(&ep->base.refcnt))
+		sctp_endpoint_destroy(ep);
+}
+
+/* Is this the endpoint we are looking for?  */
+struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *ep,
+					       const union sctp_addr *laddr)
+{
+	struct sctp_endpoint *retval;
+
+	sctp_read_lock(&ep->base.addr_lock);
+	if (ep->base.bind_addr.port == laddr->v4.sin_port) {
+		if (sctp_bind_addr_match(&ep->base.bind_addr, laddr,
+					 sctp_sk(ep->base.sk))) {
+			retval = ep;
+			goto out;
+		}
+	}
+
+	retval = NULL;
+
+out:
+	sctp_read_unlock(&ep->base.addr_lock);
+	return retval;
+}
+
+/* Find the association that goes with this chunk.
+ * We do a linear search of the associations for this endpoint.
+ * We return the matching transport address too.
+ */
+struct sctp_association *__sctp_endpoint_lookup_assoc(
+	const struct sctp_endpoint *ep,
+	const union sctp_addr *paddr,
+	struct sctp_transport **transport)
+{
+	int rport;
+	struct sctp_association *asoc;
+	struct list_head *pos;
+
+	rport = paddr->v4.sin_port;
+
+	list_for_each(pos, &ep->asocs) {
+		asoc = list_entry(pos, struct sctp_association, asocs);
+		if (rport == asoc->peer.port) {
+			sctp_read_lock(&asoc->base.addr_lock);
+			*transport = sctp_assoc_lookup_paddr(asoc, paddr);
+			sctp_read_unlock(&asoc->base.addr_lock);
+
+			if (*transport)
+				return asoc;
+		}
+	}
+
+	*transport = NULL;
+	return NULL;
+}
+
+/* Lookup association on an endpoint based on a peer address.  BH-safe.  */
+struct sctp_association *sctp_endpoint_lookup_assoc(
+	const struct sctp_endpoint *ep,
+	const union sctp_addr *paddr,
+	struct sctp_transport **transport)
+{
+	struct sctp_association *asoc;
+
+	sctp_local_bh_disable();
+	asoc = __sctp_endpoint_lookup_assoc(ep, paddr, transport);
+	sctp_local_bh_enable();
+
+	return asoc;
+}
+
+/* Look for any peeled off association from the endpoint that matches the
+ * given peer address.
+ */
+int sctp_endpoint_is_peeled_off(struct sctp_endpoint *ep,
+				const union sctp_addr *paddr)
+{
+	struct list_head *pos;
+	struct sockaddr_storage_list *addr;
+	sctp_bind_addr_t *bp;
+
+	sctp_read_lock(&ep->base.addr_lock);
+	bp = &ep->base.bind_addr;
+	list_for_each(pos, &bp->address_list) {
+		addr = list_entry(pos, struct sockaddr_storage_list, list);
+		if (sctp_has_association(&addr->a, paddr)) {
+			sctp_read_unlock(&ep->base.addr_lock);
+			return 1;
+		}
+	}
+	sctp_read_unlock(&ep->base.addr_lock);
+
+	return 0;
+}
+
+/* Do delayed input processing.  This is scheduled by sctp_rcv().
+ * This may be called on BH or task time.
+ */
+static void sctp_endpoint_bh_rcv(struct sctp_endpoint *ep)
+{
+	struct sctp_association *asoc;
+	struct sock *sk;
+	struct sctp_transport *transport;
+	sctp_chunk_t *chunk;
+	struct sctp_inq *inqueue;
+	sctp_subtype_t subtype;
+	sctp_state_t state;
+	int error = 0;
+
+	if (ep->base.dead)
+		return;
+
+	asoc = NULL;
+	inqueue = &ep->base.inqueue;
+	sk = ep->base.sk;
+
+	while (NULL != (chunk = sctp_inq_pop(inqueue))) {
+		subtype.chunk = chunk->chunk_hdr->type;
+
+		/* We might have grown an association since last we
+		 * looked, so try again.
+		 *
+		 * This happens when we've just processed our
+		 * COOKIE-ECHO chunk.
+		 */
+		if (NULL == chunk->asoc) {
+			asoc = sctp_endpoint_lookup_assoc(ep,
+							  sctp_source(chunk),
+							  &transport);
+			chunk->asoc = asoc;
+			chunk->transport = transport;
+		}
+
+		state = asoc ? asoc->state : SCTP_STATE_CLOSED;
+
+		/* Remember where the last DATA chunk came from so we
+		 * know where to send the SACK.
+		 */
+		if (asoc && sctp_chunk_is_data(chunk))
+			asoc->peer.last_data_from = chunk->transport;
+		else
+			SCTP_INC_STATS(SctpInCtrlChunks);
+
+		if (chunk->transport)
+			chunk->transport->last_time_heard = jiffies;
+
+		error = sctp_do_sm(SCTP_EVENT_T_CHUNK, subtype, state,
+                                   ep, asoc, chunk, GFP_ATOMIC);
+
+		if (error && chunk)
+			chunk->pdiscard = 1;
+
+		/* Check to see if the endpoint is freed in response to
+		 * the incoming chunk. If so, get out of the while loop.
+		 */
+		if (!sctp_sk(sk)->ep)
+			break;
+	}
+}
diff -urN linux-2.4.23-pre5/net/sctp/hashdriver.c linux-2.4.23-pre6/net/sctp/hashdriver.c
--- linux-2.4.23-pre5/net/sctp/hashdriver.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/hashdriver.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,128 @@
+/* SCTP reference Implementation Copyright (C) 1999 Cisco And Motorola
+ * 
+ * This file origiantes from Randy Stewart's SCTP reference Implementation.
+ * 
+ * The SCTP reference implementation is free software; 
+ * you can redistribute it and/or modify it under the terms of 
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ * 
+ * The SCTP reference implementation is distributed in the hope that it 
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.  
+ * 
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ * 
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by: 
+ *    Randy Stewart  rstewar1@email.mot.com
+ *    Ken Morneau    kmorneau@cisco.com
+ *    Qiaobing Xie   qxie1@email.mot.com
+ * 
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorperated into the next SCTP release.
+ * 
+ * There are still LOTS of bugs in this code... I always run on the motto
+ * "it is a wonder any code ever works :)"
+ */
+
+#include <linux/types.h>
+#include <asm/string.h>
+#include <net/sctp/sctp.h>
+#include <net/sctp/sla1.h>
+
+/* SCTP Main driver.
+ * passing a two pointers and two lengths,
+ * returning a digest pointer filled. The md5 code
+ * was taken directly from the RFC (2104) so to understand it
+ * you may want to go look at the RFC referenced in the
+ * SCTP spec. We did modify this code to either user OUR
+ * implementation of SLA1 or the MD5 that comes from its
+ * RFC. SLA1 may have IPR issues so you need to check in
+ * to this if you wish to use it... Or at least that is
+ * what the FIP-180.1 web page says.
+ */
+
+void sctp_hash_digest(const char *key, const int in_key_len,
+		      const char *text, const int text_len,
+		      __u8 *digest)
+{
+	int key_len = in_key_len;
+	struct SLA_1_Context context;
+
+	__u8 k_ipad[65];	/* inner padding -
+				 * key XORd with ipad
+				 */
+	__u8 k_opad[65];	/* outer padding -
+				 * key XORd with opad
+				 */
+	__u8 tk[20];
+	int i;
+
+	/* if key is longer than 64 bytes reset it to key=MD5(key) */
+	if (key_len > 64) {
+		struct SLA_1_Context tctx;
+
+		SLA1_Init(&tctx);
+		SLA1_Process(&tctx, key, key_len);
+		SLA1_Final(&tctx,tk);
+		key = tk;
+		key_len = 20;
+        }
+
+	/*
+	 * the HMAC_MD5 transform looks like:
+	 *
+	 * MD5(K XOR opad, MD5(K XOR ipad, text))
+	 *
+	 * where K is an n byte key
+	 * ipad is the byte 0x36 repeated 64 times
+	 * opad is the byte 0x5c repeated 64 times
+	 * and text is the data being protected
+	 */
+
+	/* start out by storing key in pads */
+	memset(k_ipad, 0, sizeof k_ipad);
+	memset(k_opad, 0, sizeof k_opad);
+	memcpy(k_ipad, key, key_len);
+	memcpy(k_opad, key, key_len);
+
+	/* XOR key with ipad and opad values */
+	for (i = 0; i < 64; i++) {
+		k_ipad[i] ^= 0x36;
+		k_opad[i] ^= 0x5c;
+	}
+
+	/* perform inner hash */
+	SLA1_Init(&context);                     /* init context for 1st
+						  * pass
+						  */
+	SLA1_Process(&context, k_ipad, 64);      /* start with inner pad */
+	SLA1_Process(&context, text, text_len);  /* then text of datagram */
+	SLA1_Final(&context,digest);             /* finish up 1st pass */
+
+	/*
+         * perform outer hash
+         */
+	SLA1_Init(&context);                   /* init context for 2nd
+						* pass
+						*/
+	SLA1_Process(&context, k_opad, 64);     /* start with outer pad */
+	SLA1_Process(&context, digest, 20);     /* then results of 1st
+						 * hash
+						 */
+	SLA1_Final(&context, digest);          /* finish up 2nd pass */
+}
+
diff -urN linux-2.4.23-pre5/net/sctp/input.c linux-2.4.23-pre6/net/sctp/input.c
--- linux-2.4.23-pre5/net/sctp/input.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/input.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,834 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001-2003 International Business Machines, Corp.
+ * Copyright (c) 2001 Intel Corp.
+ * Copyright (c) 2001 Nokia, Inc.
+ * Copyright (c) 2001 La Monte H.P. Yarroll
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * These functions handle all input from the IP layer into SCTP.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Karl Knutson <karl@athena.chicago.il.us>
+ *    Xingang Guo <xingang.guo@intel.com>
+ *    Jon Grimm <jgrimm@us.ibm.com>
+ *    Hui Huang <hui.huang@nokia.com>
+ *    Daisy Chang <daisyc@us.ibm.com>
+ *    Sridhar Samudrala <sri@us.ibm.com>
+ *    Ardelle Fan <ardelle.fan@intel.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/types.h>
+#include <linux/list.h> /* For struct list_head */
+#include <linux/socket.h>
+#include <linux/ip.h>
+#include <linux/time.h> /* For struct timeval */
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <net/snmp.h>
+#include <net/sock.h>
+#include <linux/ipsec.h>
+#include <net/sctp/sctp.h>
+#include <net/sctp/sm.h>
+
+/* Forward declarations for internal helpers. */
+static int sctp_rcv_ootb(struct sk_buff *);
+struct sctp_association *__sctp_rcv_lookup(struct sk_buff *skb,
+				      const union sctp_addr *laddr,
+				      const union sctp_addr *paddr,
+				      struct sctp_transport **transportp);
+struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr);
+
+
+/* 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));
+
+	for (; list; list = list->next)
+		val = sctp_update_cksum((__u8 *)list->data, skb_headlen(list),
+					val);
+
+	val = sctp_end_cksum(val);
+
+	if (val != cmp) {
+		/* CRC failure, dump it. */
+		SCTP_INC_STATS_BH(SctpChecksumErrors);
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * This is the routine which IP calls when receiving an SCTP packet.
+ */
+int sctp_rcv(struct sk_buff *skb)
+{
+	struct sock *sk;
+	struct sctp_association *asoc;
+	struct sctp_endpoint *ep = NULL;
+	struct sctp_ep_common *rcvr;
+	struct sctp_transport *transport = NULL;
+	sctp_chunk_t *chunk;
+	struct sctphdr *sh;
+	union sctp_addr src;
+	union sctp_addr dest;
+	int family;
+	struct sctp_af *af;
+	int ret = 0;
+
+	if (skb->pkt_type!=PACKET_HOST)
+		goto discard_it;
+
+	SCTP_INC_STATS_BH(SctpInSCTPPacks);
+
+	sh = (struct sctphdr *) skb->h.raw;
+
+	/* Pull up the IP and SCTP headers. */
+	__skb_pull(skb, skb->h.raw - skb->data);
+	if (skb->len < sizeof(struct sctphdr))
+		goto bad_packet;
+	if (sctp_rcv_checksum(skb) < 0)
+		goto bad_packet;
+
+	skb_pull(skb, sizeof(struct sctphdr));
+
+	family = ipver2af(skb->nh.iph->version);
+	af = sctp_get_af_specific(family);
+	if (unlikely(!af))
+		goto bad_packet;
+
+	/* Initialize local addresses for lookups. */
+	af->from_skb(&src, skb, 1);
+	af->from_skb(&dest, skb, 0);
+
+	/* If the packet is to or from a non-unicast address,
+	 * silently discard the packet.
+	 *
+	 * This is not clearly defined in the RFC except in section
+	 * 8.4 - OOTB handling.  However, based on the book "Stream Control
+	 * Transmission Protocol" 2.1, "It is important to note that the
+	 * IP address of an SCTP transport address must be a routable
+	 * unicast address.  In other words, IP multicast addresses and
+	 * IP broadcast addresses cannot be used in an SCTP transport
+	 * address."
+	 */
+	if (!af->addr_valid(&src) || !af->addr_valid(&dest))
+		goto discard_it;
+
+	asoc = __sctp_rcv_lookup(skb, &src, &dest, &transport);
+
+	/*
+	 * RFC 2960, 8.4 - Handle "Out of the blue" Packets.
+	 * An SCTP packet is called an "out of the blue" (OOTB)
+	 * packet if it is correctly formed, i.e., passed the
+	 * receiver's checksum check, but the receiver is not
+	 * able to identify the association to which this
+	 * packet belongs.
+	 */
+	if (!asoc) {
+		ep = __sctp_rcv_lookup_endpoint(&dest);
+		if (sctp_rcv_ootb(skb)) {
+			SCTP_INC_STATS_BH(SctpOutOfBlues);
+			goto discard_release;
+		}
+	}
+
+	/* Retrieve the common input handling substructure. */
+	rcvr = asoc ? &asoc->base : &ep->base;
+	sk = rcvr->sk;
+
+	if (!ipsec_sk_policy(sk, skb))
+		goto discard_release;
+
+	ret = sk_filter(sk, skb, 1);
+	if (ret)
+		goto discard_release;
+
+	/* Create an SCTP packet structure. */
+	chunk = sctp_chunkify(skb, asoc, sk);
+	if (!chunk) {
+		ret = -ENOMEM;
+		goto discard_release;
+	}
+
+	/* Remember what endpoint is to handle this packet. */
+	chunk->rcvr = rcvr;
+
+	/* Remember the SCTP header. */
+	chunk->sctp_hdr = sh;
+
+	/* Set the source and destination addresses of the incoming chunk.  */
+	sctp_init_addrs(chunk, &src, &dest);
+
+	/* Remember where we came from.  */
+	chunk->transport = transport;
+
+	/* Acquire access to the sock lock. Note: We are safe from other
+	 * bottom halves on this lock, but a user may be in the lock too,
+	 * so check if it is busy.
+	 */
+	sctp_bh_lock_sock(sk);
+
+	if (sock_owned_by_user(sk))
+		sk_add_backlog(sk, (struct sk_buff *) chunk);
+	else
+		sctp_backlog_rcv(sk, (struct sk_buff *) chunk);
+
+	/* Release the sock and any reference counts we took in the
+	 * lookup calls.
+	 */
+	sctp_bh_unlock_sock(sk);
+	if (asoc)
+		sctp_association_put(asoc);
+	else
+		sctp_endpoint_put(ep);
+	sock_put(sk);
+	return ret;
+
+bad_packet:
+	SCTP_INC_STATS(SctpChecksumErrors);
+
+discard_it:
+	kfree_skb(skb);
+	return ret;
+
+discard_release:
+	/* Release any structures we may be holding. */
+	if (asoc) {
+		sock_put(asoc->base.sk);
+		sctp_association_put(asoc);
+	} else {
+		sock_put(ep->base.sk);
+		sctp_endpoint_put(ep);
+	}
+
+	goto discard_it;
+}
+
+/* Handle second half of inbound skb processing.  If the sock was busy,
+ * we may have need to delay processing until later when the sock is
+ * released (on the backlog).   If not busy, we call this routine
+ * directly from the bottom half.
+ */
+int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
+{
+	sctp_chunk_t *chunk;
+	struct sctp_inq *inqueue;
+
+	/* One day chunk will live inside the skb, but for
+	 * now this works.
+	 */
+	chunk = (sctp_chunk_t *) skb;
+	inqueue = &chunk->rcvr->inqueue;
+
+	sctp_inq_push(inqueue, chunk);
+        return 0;
+}
+
+/* Handle icmp frag needed error. */
+void sctp_icmp_frag_needed(struct sock *sk, struct sctp_association *asoc,
+			   struct sctp_transport *t, __u32 pmtu)
+{
+	if (unlikely(pmtu < SCTP_DEFAULT_MINSEGMENT)) {
+		printk(KERN_WARNING "%s: Reported pmtu %d too low, "
+		       "using default minimum of %d\n", __FUNCTION__, pmtu,
+		       SCTP_DEFAULT_MINSEGMENT);
+		pmtu = SCTP_DEFAULT_MINSEGMENT;
+	}
+
+	if (!sock_owned_by_user(sk) && t && (t->pmtu != pmtu)) {
+		t->pmtu = pmtu;
+		sctp_assoc_sync_pmtu(asoc);
+		sctp_retransmit(&asoc->outqueue, t, SCTP_RTXR_PMTUD);
+	}
+}
+
+/* Common lookup code for icmp/icmpv6 error handler. */
+struct sock *sctp_err_lookup(int family, struct sk_buff *skb,
+			     struct sctphdr *sctphdr,
+			     struct sctp_endpoint **epp,
+			     struct sctp_association **app,
+			     struct sctp_transport **tpp)
+{
+	union sctp_addr saddr;
+	union sctp_addr daddr;
+	struct sctp_af *af;
+	struct sock *sk = NULL;
+	struct sctp_endpoint *ep = NULL;
+	struct sctp_association *asoc = NULL;
+	struct sctp_transport *transport = NULL;
+
+	*app = NULL; *epp = NULL; *tpp = NULL;
+
+	af = sctp_get_af_specific(family);
+	if (unlikely(!af)) {
+		return NULL;
+	}
+
+	/* Initialize local addresses for lookups. */
+	af->from_skb(&saddr, skb, 1);
+	af->from_skb(&daddr, skb, 0);
+
+	/* Look for an association that matches the incoming ICMP error
+	 * packet.
+	 */
+	asoc = __sctp_lookup_association(&saddr, &daddr, &transport);
+	if (!asoc) {
+		/* If there is no matching association, see if it matches any
+		 * endpoint. This may happen for an ICMP error generated in
+		 * response to an INIT_ACK.
+		 */
+		ep = __sctp_rcv_lookup_endpoint(&daddr);
+		if (!ep) {
+			return NULL;
+		}
+	}
+
+	if (asoc) {
+		if (ntohl(sctphdr->vtag) != asoc->c.peer_vtag) {
+			ICMP_INC_STATS_BH(IcmpInErrors);
+			goto out;
+		}
+		sk = asoc->base.sk;
+	} else
+		sk = ep->base.sk;
+
+	sctp_bh_lock_sock(sk);
+
+	/* If too many ICMPs get dropped on busy
+	 * servers this needs to be solved differently.
+	 */
+	if (sock_owned_by_user(sk))
+		NET_INC_STATS_BH(LockDroppedIcmps);
+
+	*epp = ep;
+	*app = asoc;
+	*tpp = transport;
+	return sk;
+
+out:
+	sock_put(sk);
+	if (asoc)
+		sctp_association_put(asoc);
+	if (ep)
+		sctp_endpoint_put(ep);
+	return NULL;
+}
+
+/* Common cleanup code for icmp/icmpv6 error handler. */
+void sctp_err_finish(struct sock *sk, struct sctp_endpoint *ep,
+		     struct sctp_association *asoc)
+{
+	sctp_bh_unlock_sock(sk);
+	sock_put(sk);
+	if (asoc)
+		sctp_association_put(asoc);
+	if (ep)
+		sctp_endpoint_put(ep);
+}
+
+/*
+ * This routine is called by the ICMP module when it gets some
+ * sort of error condition.  If err < 0 then the socket should
+ * be closed and the error returned to the user.  If err > 0
+ * it's just the icmp type << 8 | icmp code.  After adjustment
+ * header points to the first 8 bytes of the sctp header.  We need
+ * to find the appropriate port.
+ *
+ * The locking strategy used here is very "optimistic". When
+ * someone else accesses the socket the ICMP is just dropped
+ * and for some paths there is no check at all.
+ * A more general error queue to queue errors for later handling
+ * is probably better.
+ *
+ */
+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;
+	struct sock *sk;
+	struct sctp_endpoint *ep;
+	struct sctp_association *asoc;
+	struct sctp_transport *transport;
+	struct inet_opt *inet;
+	char *saveip, *savesctp;
+	int err;
+
+	if (skb->len < ((iph->ihl << 2) + 8)) {
+		ICMP_INC_STATS_BH(IcmpInErrors);
+		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, &ep, &asoc, &transport);
+	/* Put back, the original pointers. */
+	skb->nh.raw = saveip;
+	skb->h.raw = savesctp;
+	if (!sk) {
+		ICMP_INC_STATS_BH(IcmpInErrors);
+		return;
+	}
+	/* Warning:  The sock lock is held.  Remember to call
+	 * sctp_err_finish!
+	 */
+
+	switch (type) {
+	case ICMP_PARAMETERPROB:
+		err = EPROTO;
+		break;
+	case ICMP_DEST_UNREACH:
+		if (code > NR_ICMP_UNREACH)
+			goto out_unlock;
+
+		/* PMTU discovery (RFC1191) */
+		if (ICMP_FRAG_NEEDED == code) {
+			sctp_icmp_frag_needed(sk, asoc, transport, info);
+			goto out_unlock;
+		}
+
+		err = icmp_err_convert[code].errno;
+		break;
+	case ICMP_TIME_EXCEEDED:
+		/* Ignore any time exceeded errors due to fragment reassembly
+		 * timeouts.
+		 */
+		if (ICMP_EXC_FRAGTIME == code)
+			goto out_unlock;
+
+		err = EHOSTUNREACH;
+		break;
+	default:
+		goto out_unlock;
+	}
+
+	inet = inet_sk(sk);
+	if (!sock_owned_by_user(sk) && inet->recverr) {
+		sk->err = err;
+		sk->error_report(sk);
+	} else {  /* Only an error on timeout */
+		sk->err_soft = err;
+	}
+
+out_unlock:
+	sctp_err_finish(sk, ep, asoc);
+}
+
+/*
+ * RFC 2960, 8.4 - Handle "Out of the blue" Packets.
+ *
+ * This function scans all the chunks in the OOTB packet to determine if
+ * the packet should be discarded right away.  If a response might be needed
+ * for this packet, or, if further processing is possible, the packet will
+ * be queued to a proper inqueue for the next phase of handling.
+ *
+ * Output:
+ * Return 0 - If further processing is needed.
+ * Return 1 - If the packet can be discarded right away.
+ */
+int sctp_rcv_ootb(struct sk_buff *skb)
+{
+	sctp_chunkhdr_t *ch;
+	__u8 *ch_end;
+	sctp_errhdr_t *err;
+
+	ch = (sctp_chunkhdr_t *) skb->data;
+
+	/* Scan through all the chunks in the packet.  */
+	do {
+		ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length));
+
+		/* RFC 8.4, 2) If the OOTB packet contains an ABORT chunk, the
+		 * receiver MUST silently discard the OOTB packet and take no
+		 * further action.
+		 */
+		if (SCTP_CID_ABORT == ch->type)
+			goto discard;
+
+		/* RFC 8.4, 6) If the packet contains a SHUTDOWN COMPLETE
+		 * chunk, the receiver should silently discard the packet
+		 * and take no further action.
+		 */
+		if (SCTP_CID_SHUTDOWN_COMPLETE == ch->type)
+			goto discard;
+
+		/* RFC 8.4, 7) If the packet contains a "Stale cookie" ERROR
+		 * or a COOKIE ACK the SCTP Packet should be silently
+		 * discarded.
+		 */
+		if (SCTP_CID_COOKIE_ACK == ch->type)
+			goto discard;
+
+		if (SCTP_CID_ERROR == ch->type) {
+			err = (sctp_errhdr_t *)(ch + sizeof(sctp_chunkhdr_t));
+			if (SCTP_ERROR_STALE_COOKIE == err->cause)
+				goto discard;
+		}
+
+		ch = (sctp_chunkhdr_t *) ch_end;
+	} while (ch_end < skb->tail);
+
+	return 0;
+
+discard:
+	return 1;
+}
+
+/* Insert endpoint into the hash table.  */
+void __sctp_hash_endpoint(struct sctp_endpoint *ep)
+{
+	struct sctp_ep_common **epp;
+	struct sctp_ep_common *epb;
+	sctp_hashbucket_t *head;
+
+	epb = &ep->base;
+
+	epb->hashent = sctp_ep_hashfn(epb->bind_addr.port);
+	head = &sctp_proto.ep_hashbucket[epb->hashent];
+
+	sctp_write_lock(&head->lock);
+	epp = &head->chain;
+	epb->next = *epp;
+	if (epb->next)
+		(*epp)->pprev = &epb->next;
+	*epp = epb;
+	epb->pprev = epp;
+	sctp_write_unlock(&head->lock);
+}
+
+/* Add an endpoint to the hash. Local BH-safe. */
+void sctp_hash_endpoint(struct sctp_endpoint *ep)
+{
+	sctp_local_bh_disable();
+	__sctp_hash_endpoint(ep);
+	sctp_local_bh_enable();
+}
+
+/* Remove endpoint from the hash table.  */
+void __sctp_unhash_endpoint(struct sctp_endpoint *ep)
+{
+	sctp_hashbucket_t *head;
+	struct sctp_ep_common *epb;
+
+	epb = &ep->base;
+
+	epb->hashent = sctp_ep_hashfn(epb->bind_addr.port);
+
+	head = &sctp_proto.ep_hashbucket[epb->hashent];
+
+	sctp_write_lock(&head->lock);
+
+	if (epb->pprev) {
+		if (epb->next)
+			epb->next->pprev = epb->pprev;
+		*epb->pprev = epb->next;
+		epb->pprev = NULL;
+	}
+
+	sctp_write_unlock(&head->lock);
+}
+
+/* Remove endpoint from the hash.  Local BH-safe. */
+void sctp_unhash_endpoint(struct sctp_endpoint *ep)
+{
+	sctp_local_bh_disable();
+	__sctp_unhash_endpoint(ep);
+	sctp_local_bh_enable();
+}
+
+/* Look up an endpoint. */
+struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr)
+{
+	sctp_hashbucket_t *head;
+	struct sctp_ep_common *epb;
+	struct sctp_endpoint *ep;
+	int hash;
+
+	hash = sctp_ep_hashfn(laddr->v4.sin_port);
+	head = &sctp_proto.ep_hashbucket[hash];
+	read_lock(&head->lock);
+	for (epb = head->chain; epb; epb = epb->next) {
+		ep = sctp_ep(epb);
+		if (sctp_endpoint_is_match(ep, laddr))
+			goto hit;
+	}
+
+	ep = sctp_sk((sctp_get_ctl_sock()))->ep;
+	epb = &ep->base;
+
+hit:
+	sctp_endpoint_hold(ep);
+	sock_hold(epb->sk);
+	read_unlock(&head->lock);
+	return ep;
+}
+
+/* Add an association to the hash. Local BH-safe. */
+void sctp_hash_established(struct sctp_association *asoc)
+{
+	sctp_local_bh_disable();
+	__sctp_hash_established(asoc);
+	sctp_local_bh_enable();
+}
+
+/* Insert association into the hash table.  */
+void __sctp_hash_established(struct sctp_association *asoc)
+{
+	struct sctp_ep_common **epp;
+	struct sctp_ep_common *epb;
+	sctp_hashbucket_t *head;
+
+	epb = &asoc->base;
+
+	/* Calculate which chain this entry will belong to. */
+	epb->hashent = sctp_assoc_hashfn(epb->bind_addr.port, asoc->peer.port);
+
+	head = &sctp_proto.assoc_hashbucket[epb->hashent];
+
+	sctp_write_lock(&head->lock);
+	epp = &head->chain;
+	epb->next = *epp;
+	if (epb->next)
+		(*epp)->pprev = &epb->next;
+	*epp = epb;
+	epb->pprev = epp;
+	sctp_write_unlock(&head->lock);
+}
+
+/* Remove association from the hash table.  Local BH-safe. */
+void sctp_unhash_established(struct sctp_association *asoc)
+{
+	sctp_local_bh_disable();
+	__sctp_unhash_established(asoc);
+	sctp_local_bh_enable();
+}
+
+/* Remove association from the hash table.  */
+void __sctp_unhash_established(struct sctp_association *asoc)
+{
+	sctp_hashbucket_t *head;
+	struct sctp_ep_common *epb;
+
+	epb = &asoc->base;
+
+	epb->hashent = sctp_assoc_hashfn(epb->bind_addr.port,
+					 asoc->peer.port);
+
+	head = &sctp_proto.assoc_hashbucket[epb->hashent];
+
+	sctp_write_lock(&head->lock);
+
+	if (epb->pprev) {
+		if (epb->next)
+			epb->next->pprev = epb->pprev;
+		*epb->pprev = epb->next;
+		epb->pprev = NULL;
+	}
+
+	sctp_write_unlock(&head->lock);
+}
+
+/* Look up an association. */
+struct sctp_association *__sctp_lookup_association(
+					const union sctp_addr *local,
+					const union sctp_addr *peer,
+					struct sctp_transport **pt)
+{
+	sctp_hashbucket_t *head;
+	struct sctp_ep_common *epb;
+	struct sctp_association *asoc;
+	struct sctp_transport *transport;
+	int hash;
+
+	/* Optimize here for direct hit, only listening connections can
+	 * have wildcards anyways.
+	 */
+	hash = sctp_assoc_hashfn(local->v4.sin_port, peer->v4.sin_port);
+	head = &sctp_proto.assoc_hashbucket[hash];
+	read_lock(&head->lock);
+	for (epb = head->chain; epb; epb = epb->next) {
+		asoc = sctp_assoc(epb);
+		transport = sctp_assoc_is_match(asoc, local, peer);
+		if (transport)
+			goto hit;
+	}
+
+	read_unlock(&head->lock);
+
+	return NULL;
+
+hit:
+	*pt = transport;
+	sctp_association_hold(asoc);
+	sock_hold(epb->sk);
+	read_unlock(&head->lock);
+	return asoc;
+}
+
+/* Look up an association. BH-safe. */
+struct sctp_association *sctp_lookup_association(const union sctp_addr *laddr,
+					    const union sctp_addr *paddr,
+					    struct sctp_transport **transportp)
+{
+	struct sctp_association *asoc;
+
+	sctp_local_bh_disable();
+	asoc = __sctp_lookup_association(laddr, paddr, transportp);
+	sctp_local_bh_enable();
+
+	return asoc;
+}
+
+/* Is there an association matching the given local and peer addresses? */
+int sctp_has_association(const union sctp_addr *laddr,
+			 const union sctp_addr *paddr)
+{
+	struct sctp_association *asoc;
+	struct sctp_transport *transport;
+
+	if ((asoc = sctp_lookup_association(laddr, paddr, &transport))) {
+		sock_put(asoc->base.sk);
+		sctp_association_put(asoc);
+		return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * SCTP Implementors Guide, 2.18 Handling of address
+ * parameters within the INIT or INIT-ACK.
+ *
+ * D) When searching for a matching TCB upon reception of an INIT
+ *    or INIT-ACK chunk the receiver SHOULD use not only the
+ *    source address of the packet (containing the INIT or
+ *    INIT-ACK) but the receiver SHOULD also use all valid
+ *    address parameters contained within the chunk.
+ *
+ * 2.18.3 Solution description
+ *
+ * This new text clearly specifies to an implementor the need
+ * to look within the INIT or INIT-ACK. Any implementation that
+ * does not do this, may not be able to establish associations
+ * in certain circumstances.
+ *
+ */
+static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb,
+	const union sctp_addr *laddr, struct sctp_transport **transportp)
+{
+	struct sctp_association *asoc;
+	union sctp_addr addr;
+	union sctp_addr *paddr = &addr;
+	struct sctphdr *sh = (struct sctphdr *) skb->h.raw;
+	sctp_chunkhdr_t *ch;
+	union sctp_params params;
+	sctp_init_chunk_t *init;
+
+	ch = (sctp_chunkhdr_t *) skb->data;
+
+	/* If this is INIT/INIT-ACK look inside the chunk too. */
+	switch (ch->type) {
+	case SCTP_CID_INIT:
+	case SCTP_CID_INIT_ACK:
+		break;
+	default:
+		return NULL;
+	}
+
+	/*
+	 * This code will NOT touch anything inside the chunk--it is
+	 * strictly READ-ONLY.
+	 *
+	 * RFC 2960 3  SCTP packet Format
+	 *
+	 * Multiple chunks can be bundled into one SCTP packet up to
+	 * the MTU size, except for the INIT, INIT ACK, and SHUTDOWN
+	 * COMPLETE chunks.  These chunks MUST NOT be bundled with any
+	 * other chunk in a packet.  See Section 6.10 for more details
+	 * on chunk bundling.
+	 */
+
+	/* Find the start of the TLVs and the end of the chunk.  This is
+	 * the region we search for address parameters.
+	 */
+	init = (sctp_init_chunk_t *)skb->data;
+
+	/* Walk the parameters looking for embedded addresses. */
+	sctp_walk_params(params, init, init_hdr.params) {
+
+		/* Note: Ignoring hostname addresses. */
+		if ((SCTP_PARAM_IPV4_ADDRESS != params.p->type) &&
+		    (SCTP_PARAM_IPV6_ADDRESS != params.p->type))
+			continue;
+
+		sctp_param2sockaddr(paddr, params.addr, ntohs(sh->source), 0);
+		asoc = __sctp_lookup_association(laddr, paddr, transportp);
+		if (asoc)
+			return asoc;
+	}
+
+	return NULL;
+}
+
+/* Lookup an association for an inbound skb. */
+struct sctp_association *__sctp_rcv_lookup(struct sk_buff *skb,
+				      const union sctp_addr *paddr,
+				      const union sctp_addr *laddr,
+				      struct sctp_transport **transportp)
+{
+	struct sctp_association *asoc;
+
+	asoc = __sctp_lookup_association(laddr, paddr, transportp);
+
+	/* Further lookup for INIT/INIT-ACK packets.
+	 * SCTP Implementors Guide, 2.18 Handling of address
+	 * parameters within the INIT or INIT-ACK.
+	 */
+	if (!asoc)
+		asoc = __sctp_rcv_init_lookup(skb, laddr, transportp);
+
+	return asoc;
+}
diff -urN linux-2.4.23-pre5/net/sctp/inqueue.c linux-2.4.23-pre6/net/sctp/inqueue.c
--- linux-2.4.23-pre5/net/sctp/inqueue.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/inqueue.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,199 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2002 International Business Machines, Corp.
+ * 
+ * This file is part of the SCTP kernel reference Implementation
+ * 
+ * These functions are the methods for accessing the SCTP inqueue.
+ *
+ * An SCTP inqueue is a queue into which you push SCTP packets
+ * (which might be bundles or fragments of chunks) and out of which you
+ * pop SCTP whole chunks.
+ * 
+ * The SCTP reference implementation is free software; 
+ * you can redistribute it and/or modify it under the terms of 
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ * 
+ * The SCTP reference implementation is distributed in the hope that it 
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.  
+ * 
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ * 
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by: 
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Karl Knutson <karl@athena.chicago.il.us>
+ * 
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <net/sctp/sctp.h>
+#include <net/sctp/sm.h>
+#include <linux/interrupt.h>
+
+/* Initialize an SCTP inqueue.  */
+void sctp_inq_init(struct sctp_inq *queue)
+{
+	skb_queue_head_init(&queue->in);
+	queue->in_progress = NULL;
+
+	/* Create a task for delivering data.  */
+	INIT_LIST_HEAD(&queue->immediate.list);
+	queue->immediate.sync = 0;
+	queue->immediate.routine = NULL;
+	queue->immediate.data = NULL;
+
+	queue->malloced = 0;
+}
+
+/* Create an initialized sctp_inq.  */
+struct sctp_inq *sctp_inq_new(void)
+{
+	struct sctp_inq *retval;
+
+	retval = t_new(struct sctp_inq, GFP_ATOMIC);
+	if (retval) {
+		sctp_inq_init(retval);
+		retval->malloced = 1;
+	}
+        return retval;
+}
+
+/* Release the memory associated with an SCTP inqueue.  */
+void sctp_inq_free(struct sctp_inq *queue)
+{
+	sctp_chunk_t *chunk;
+
+	/* Empty the queue.  */
+	while ((chunk = (sctp_chunk_t *) skb_dequeue(&queue->in)))
+		sctp_free_chunk(chunk);
+
+	/* If there is a packet which is currently being worked on,
+	 * free it as well.
+	 */
+	if (queue->in_progress)
+		sctp_free_chunk(queue->in_progress);
+
+	if (queue->malloced) {
+		/* Dump the master memory segment.  */
+		kfree(queue);
+	}
+}
+
+/* Put a new packet in an SCTP inqueue.
+ * We assume that packet->sctp_hdr is set and in host byte order.
+ */
+void sctp_inq_push(struct sctp_inq *q, sctp_chunk_t *packet)
+{
+	/* Directly call the packet handling routine. */
+
+	/* We are now calling this either from the soft interrupt
+	 * or from the backlog processing.
+	 * Eventually, we should clean up inqueue to not rely
+	 * on the BH related data structures.
+	 */
+	skb_queue_tail(&(q->in), (struct sk_buff *) packet);
+	q->immediate.routine(q->immediate.data);
+}
+
+/* Extract a chunk from an SCTP inqueue.
+ *
+ * WARNING:  If you need to put the chunk on another queue, you need to
+ * make a shallow copy (clone) of it.
+ */
+sctp_chunk_t *sctp_inq_pop(struct sctp_inq *queue)
+{
+	sctp_chunk_t *chunk;
+	sctp_chunkhdr_t *ch = NULL;
+
+	/* The assumption is that we are safe to process the chunks
+	 * at this time.
+	 */
+
+	if ((chunk = queue->in_progress)) {
+		/* There is a packet that we have been working on.
+		 * Any post processing work to do before we move on?
+		 */
+		if (chunk->singleton ||
+		    chunk->end_of_packet ||
+		    chunk->pdiscard) {
+			sctp_free_chunk(chunk);
+			chunk = queue->in_progress = NULL;
+		} else {
+			/* Nothing to do. Next chunk in the packet, please. */
+			ch = (sctp_chunkhdr_t *) chunk->chunk_end;
+
+			/* Force chunk->skb->data to chunk->chunk_end.  */
+			skb_pull(chunk->skb,
+				 chunk->chunk_end - chunk->skb->data);
+		}
+	}
+
+	/* Do we need to take the next packet out of the queue to process? */
+	if (!chunk) {
+		/* Is the queue empty?  */
+        	if (skb_queue_empty(&queue->in))
+			return NULL;
+
+		chunk = queue->in_progress =
+			(sctp_chunk_t *) skb_dequeue(&queue->in);
+
+		/* This is the first chunk in the packet.  */
+		chunk->singleton = 1;
+		ch = (sctp_chunkhdr_t *) chunk->skb->data;
+	}
+
+        chunk->chunk_hdr = ch;
+        chunk->chunk_end = ((__u8 *) ch)
+		+ WORD_ROUND(ntohs(ch->length));
+	skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t));
+	chunk->subh.v = NULL; /* Subheader is no longer valid.  */
+
+	if (chunk->chunk_end < chunk->skb->tail) {
+		/* This is not a singleton */
+		chunk->singleton = 0;
+	} else {
+		/* We are at the end of the packet, so mark the chunk
+		 * in case we need to send a SACK.
+		 */
+		chunk->end_of_packet = 1;
+	}
+
+	SCTP_DEBUG_PRINTK("+++sctp_inq_pop+++ chunk %p[%s],"
+			  " length %d, skb->len %d\n",chunk,
+			  sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)),
+			  ntohs(chunk->chunk_hdr->length), chunk->skb->len);
+	return chunk;
+}
+
+/* Set a top-half handler.
+ *
+ * Originally, we the top-half handler was scheduled as a BH.  We now
+ * call the handler directly in sctp_inq_push() at a time that
+ * we know we are lock safe.
+ * The intent is that this routine will pull stuff out of the
+ * inqueue and process it.
+ */
+void sctp_inq_set_th_handler(struct sctp_inq *q,
+				 void (*callback)(void *), void *arg)
+{
+	q->immediate.routine = callback;
+	q->immediate.data = arg;
+}
+
diff -urN linux-2.4.23-pre5/net/sctp/ipv6.c linux-2.4.23-pre6/net/sctp/ipv6.c
--- linux-2.4.23-pre5/net/sctp/ipv6.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/ipv6.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,868 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 2001 Nokia, Inc.
+ * Copyright (c) 2001 La Monte H.P. Yarroll
+ * Copyright (c) 2002-2003 International Business Machines, Corp.
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * SCTP over IPv6.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    Le Yanqun             <yanqun.le@nokia.com>
+ *    Hui Huang		    <hui.huang@nokia.com>
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Sridhar Samudrala	    <sri@us.ibm.com>
+ *    Jon Grimm             <jgrimm@us.ibm.com>
+ *
+ * Based on:
+ *      linux/net/ipv6/tcp_ipv6.c
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/sched.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/netdevice.h>
+#include <linux/init.h>
+#include <linux/ipsec.h>
+
+#include <linux/ipv6.h>
+#include <linux/icmpv6.h>
+#include <linux/random.h>
+
+#include <net/protocol.h>
+#include <net/tcp.h>
+#include <net/ndisc.h>
+#include <net/ipv6.h>
+#include <net/transp_v6.h>
+#include <net/addrconf.h>
+#include <net/ip6_route.h>
+#include <net/inet_common.h>
+#include <net/inet_ecn.h>
+#include <net/sctp/sctp.h>
+
+#include <asm/uaccess.h>
+
+extern struct notifier_block sctp_inetaddr_notifier;
+
+/* FIXME: This macro needs to be moved to a common header file. */
+#define NIP6(addr) \
+        ntohs((addr)->s6_addr16[0]), \
+        ntohs((addr)->s6_addr16[1]), \
+        ntohs((addr)->s6_addr16[2]), \
+        ntohs((addr)->s6_addr16[3]), \
+        ntohs((addr)->s6_addr16[4]), \
+        ntohs((addr)->s6_addr16[5]), \
+        ntohs((addr)->s6_addr16[6]), \
+        ntohs((addr)->s6_addr16[7])
+
+/* ICMP error handler. */
+void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+		 int type, int code, int offset, __u32 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_endpoint *ep;
+	struct sctp_association *asoc;
+	struct sctp_transport *transport;
+	struct ipv6_pinfo *np;
+	char *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, &ep, &asoc, &transport);
+	/* Put back, the original pointers. */
+	skb->nh.raw = saveip;
+	skb->h.raw = savesctp;
+	if (!sk) {
+		ICMP6_INC_STATS_BH(Icmp6InErrors);
+		goto out;
+	}
+
+	/* Warning:  The sock lock is held.  Remember to call
+	 * sctp_err_finish!
+	 */
+
+	switch (type) {
+	case ICMPV6_PKT_TOOBIG:
+		sctp_icmp_frag_needed(sk, asoc, transport, ntohl(info));
+		goto out_unlock;
+	default:
+		break;
+	}
+
+	np = inet6_sk(sk);
+	icmpv6_err_convert(type, code, &err);
+	if (!sock_owned_by_user(sk) && np->recverr) {
+		sk->err = err;
+		sk->error_report(sk);
+	} else {  /* Only an error on timeout */
+		sk->err_soft = err;
+	}
+
+out_unlock:
+	sctp_err_finish(sk, ep, asoc);
+out:
+	if (likely(idev != NULL))
+		in6_dev_put(idev);
+}
+
+/* Based on tcp_v6_xmit() in tcp_ipv6.c. */
+static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport,
+			int ipfragok)
+{
+	struct sock *sk = skb->sk;
+	struct ipv6_pinfo *np = inet6_sk(sk);
+	struct flowi fl;
+
+	fl.proto = sk->protocol;
+
+	/* Fill in the dest address from the route entry passed with the skb
+	 * and the source address from the transport.
+	 */
+	fl.fl6_dst = &transport->ipaddr.v6.sin6_addr;
+	fl.fl6_src = &transport->saddr.v6.sin6_addr;
+
+	fl.fl6_flowlabel = np->flow_label;
+	IP6_ECN_flow_xmit(sk, fl.fl6_flowlabel);
+	if (ipv6_addr_type(fl.fl6_src) & IPV6_ADDR_LINKLOCAL)
+		fl.oif = transport->saddr.v6.sin6_scope_id;
+	else
+		fl.oif = sk->bound_dev_if;
+	fl.uli_u.ports.sport = (sk)->sport;
+	fl.uli_u.ports.dport = transport->ipaddr.v6.sin6_port;
+
+	if (np->opt && np->opt->srcrt) {
+		struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
+		fl.nl_u.ip6_u.daddr = rt0->addr;
+	}
+
+	SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, "
+			  "src:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x "
+			  "dst:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+			  __FUNCTION__, skb, skb->len, NIP6(fl.fl6_src),
+			  NIP6(fl.fl6_dst));
+
+	SCTP_INC_STATS(SctpOutSCTPPacks);
+
+	return ip6_xmit(sk, skb, &fl, np->opt);
+}
+
+/* Returns the dst cache entry for the given source and destination ip
+ * addresses.
+ */
+struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
+				  union sctp_addr *daddr,
+				  union sctp_addr *saddr)
+{
+	struct dst_entry *dst;
+	struct flowi fl = {
+		.nl_u = { .ip6_u = { .daddr = &daddr->v6.sin6_addr, } } };
+
+	SCTP_DEBUG_PRINTK("%s: DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ",
+			  __FUNCTION__, NIP6(fl.fl6_dst));
+
+	if (saddr) {
+		fl.fl6_src = &saddr->v6.sin6_addr;
+		SCTP_DEBUG_PRINTK(
+			"SRC=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x - ",
+			NIP6(fl.fl6_src));
+	}
+
+	dst = ip6_route_output(NULL, &fl);
+	if (dst) {
+		struct rt6_info *rt;
+		rt = (struct rt6_info *)dst;
+		SCTP_DEBUG_PRINTK(
+			"rt6_dst:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x "
+			"rt6_src:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+			NIP6(&rt->rt6i_dst.addr), NIP6(&rt->rt6i_src.addr));
+	} else {
+		SCTP_DEBUG_PRINTK("NO ROUTE\n");
+	}
+
+	return dst;
+}
+
+/* Returns the number of consecutive initial bits that match in the 2 ipv6
+ * addresses.
+ */
+static inline int sctp_v6_addr_match_len(union sctp_addr *s1,
+					 union sctp_addr *s2)
+{
+	struct in6_addr *a1 = &s1->v6.sin6_addr;
+	struct in6_addr *a2 = &s2->v6.sin6_addr;
+	int i, j;
+
+	for (i = 0; i < 4 ; i++) {
+		__u32 a1xora2;
+
+		a1xora2 = a1->s6_addr32[i] ^ a2->s6_addr32[i];
+
+		if ((j = fls(ntohl(a1xora2))))
+			return (i * 32 + 32 - j);
+	}
+
+	return (i*32);
+}
+
+/* Fills in the source address(saddr) based on the destination address(daddr)
+ * and asoc's bind address list.
+ */
+void sctp_v6_get_saddr(struct sctp_association *asoc, struct dst_entry *dst,
+		       union sctp_addr *daddr, union sctp_addr *saddr)
+{
+	sctp_bind_addr_t *bp;
+	rwlock_t *addr_lock;
+	struct sockaddr_storage_list *laddr;
+	struct list_head *pos;
+	sctp_scope_t scope;
+	union sctp_addr *baddr = NULL;
+	__u8 matchlen = 0;
+	__u8 bmatchlen;
+
+	SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p "
+			  "daddr:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ",
+			  __FUNCTION__, asoc, dst, NIP6(&daddr->v6.sin6_addr));
+
+	if (!asoc) {
+		ipv6_get_saddr(dst, &daddr->v6.sin6_addr,&saddr->v6.sin6_addr);
+		SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: "
+				  "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+				  NIP6(&saddr->v6.sin6_addr));
+		return;
+	}
+
+	scope = sctp_scope(daddr);
+
+	bp = &asoc->base.bind_addr;
+	addr_lock = &asoc->base.addr_lock;
+
+	/* Go through the bind address list and find the best source address
+	 * that matches the scope of the destination address.
+	 */
+	sctp_read_lock(addr_lock);
+	list_for_each(pos, &bp->address_list) {
+		laddr = list_entry(pos, struct sockaddr_storage_list, list);
+		if ((laddr->a.sa.sa_family == AF_INET6) &&
+		    (scope <= sctp_scope(&laddr->a))) {
+			bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a);
+			if (!baddr || (matchlen < bmatchlen)) {
+				baddr = &laddr->a;
+				matchlen = bmatchlen;
+			}
+		}
+	}
+
+	if (baddr) {
+		memcpy(saddr, baddr, sizeof(union sctp_addr));
+		SCTP_DEBUG_PRINTK("saddr: "
+				  "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+				  NIP6(&saddr->v6.sin6_addr));
+	} else {
+		printk(KERN_ERR "%s: asoc:%p Could not find a valid source "
+		       "address for the "
+		       "dest:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+		       __FUNCTION__, asoc, NIP6(&daddr->v6.sin6_addr));
+	}
+
+	sctp_read_unlock(addr_lock);
+}
+
+/* Make a copy of all potential local addresses. */
+static void sctp_v6_copy_addrlist(struct list_head *addrlist,
+				  struct net_device *dev)
+{
+	struct inet6_dev *in6_dev;
+	struct inet6_ifaddr *ifp;
+	struct sockaddr_storage_list *addr;
+
+	read_lock(&addrconf_lock);
+	if ((in6_dev = __in6_dev_get(dev)) == NULL) {
+		read_unlock(&addrconf_lock);
+		return;
+	}
+
+	read_lock(&in6_dev->lock);
+	for (ifp = in6_dev->addr_list; ifp; ifp = ifp->if_next) {
+		/* Add the address to the local list.  */
+		addr = t_new(struct sockaddr_storage_list, GFP_ATOMIC);
+		if (addr) {
+			addr->a.v6.sin6_family = AF_INET6;
+			addr->a.v6.sin6_port = 0;
+			addr->a.v6.sin6_addr = ifp->addr;
+			addr->a.v6.sin6_scope_id = dev->ifindex;
+			INIT_LIST_HEAD(&addr->list);
+			list_add_tail(&addr->list, addrlist);
+		}
+	}
+
+	read_unlock(&in6_dev->lock);
+	read_unlock(&addrconf_lock);
+}
+
+/* Initialize a sockaddr_storage from in incoming skb. */
+static void sctp_v6_from_skb(union sctp_addr *addr,struct sk_buff *skb,
+			     int is_saddr)
+{
+	void *from;
+	__u16 *port;
+	struct sctphdr *sh;
+
+	port = &addr->v6.sin6_port;
+	addr->v6.sin6_family = AF_INET6;
+	addr->v6.sin6_flowinfo = 0; /* FIXME */
+	addr->v6.sin6_scope_id = ((struct inet6_skb_parm *)skb->cb)->iif;
+
+	sh = (struct sctphdr *) skb->h.raw;
+	if (is_saddr) {
+		*port  = ntohs(sh->source);
+		from = &skb->nh.ipv6h->saddr;
+	} else {
+		*port = ntohs(sh->dest);
+		from = &skb->nh.ipv6h->daddr;
+	}
+	ipv6_addr_copy(&addr->v6.sin6_addr, from);
+}
+
+/* Initialize an sctp_addr from a socket. */
+static void sctp_v6_from_sk(union sctp_addr *addr, struct sock *sk)
+{
+	addr->v6.sin6_family = AF_INET6;
+	addr->v6.sin6_port = (sk)->num;
+	addr->v6.sin6_addr = inet6_sk(sk)->rcv_saddr;
+}
+
+/* Initialize sk->rcv_saddr from sctp_addr. */
+static void sctp_v6_to_sk_saddr(union sctp_addr *addr, struct sock *sk)
+{
+	inet6_sk(sk)->rcv_saddr = addr->v6.sin6_addr;
+}
+
+/* Initialize sk->daddr from sctp_addr. */
+static void sctp_v6_to_sk_daddr(union sctp_addr *addr, struct sock *sk)
+{
+	inet6_sk(sk)->daddr = addr->v6.sin6_addr;
+}
+
+/* Initialize a sctp_addr from a dst_entry. */
+static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst,
+			      unsigned short port)
+{
+	struct rt6_info *rt = (struct rt6_info *)dst;
+	addr->sa.sa_family = AF_INET6;
+	addr->v6.sin6_port = port;
+	ipv6_addr_copy(&addr->v6.sin6_addr, &rt->rt6i_src.addr);
+}
+
+/* Compare addresses exactly.
+ * FIXME: v4-mapped-v6.
+ */
+static int sctp_v6_cmp_addr(const union sctp_addr *addr1,
+			    const union sctp_addr *addr2)
+{
+	if (addr1->sa.sa_family != addr2->sa.sa_family)
+		return 0;
+	if (ipv6_addr_cmp(&addr1->v6.sin6_addr, &addr2->v6.sin6_addr))
+		return 0;
+	/* If this is a linklocal address, compare the scope_id. */
+	if (ipv6_addr_type(&addr1->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) {
+		if (addr1->v6.sin6_scope_id && addr2->v6.sin6_scope_id &&
+		    (addr1->v6.sin6_scope_id != addr2->v6.sin6_scope_id)) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+/* Initialize addr struct to INADDR_ANY. */
+static void sctp_v6_inaddr_any(union sctp_addr *addr, unsigned short port)
+{
+	memset(addr, 0x00, sizeof(union sctp_addr));
+	addr->v6.sin6_family = AF_INET6;
+	addr->v6.sin6_port = port;
+}
+
+/* Is this a wildcard address? */
+static int sctp_v6_is_any(const union sctp_addr *addr)
+{
+	int type;
+	type = ipv6_addr_type((struct in6_addr *)&addr->v6.sin6_addr);
+	return IPV6_ADDR_ANY == type;
+}
+
+/* Should this be available for binding?   */
+static int sctp_v6_available(const union sctp_addr *addr)
+{
+	int type;
+	struct in6_addr *in6 = (struct in6_addr *)&addr->v6.sin6_addr;
+
+	type = ipv6_addr_type(in6);
+	if (IPV6_ADDR_ANY == type)
+		return 1;
+	if (!(type & IPV6_ADDR_UNICAST))
+		return 0;
+
+	return ipv6_chk_addr(in6, NULL);
+}
+
+/* This function checks if the address is a valid address to be used for
+ * SCTP.
+ *
+ * Output:
+ * Return 0 - If the address is a non-unicast or an illegal address.
+ * Return 1 - If the address is a unicast.
+ */
+static int sctp_v6_addr_valid(union sctp_addr *addr)
+{
+	int ret = ipv6_addr_type(&addr->v6.sin6_addr);
+
+	/* FIXME:  v4-mapped-v6 address support. */
+
+	/* Is this a non-unicast address */
+	if (!(ret & IPV6_ADDR_UNICAST))
+		return 0;
+
+	return 1;
+}
+
+/* What is the scope of 'addr'?  */
+static sctp_scope_t sctp_v6_scope(union sctp_addr *addr)
+{
+	int v6scope;
+	sctp_scope_t retval;
+
+	/* The IPv6 scope is really a set of bit fields.
+	 * See IFA_* in <net/if_inet6.h>.  Map to a generic SCTP scope.
+	 */
+
+	v6scope = ipv6_addr_scope(&addr->v6.sin6_addr);
+	switch (v6scope) {
+	case IFA_HOST:
+		retval = SCTP_SCOPE_LOOPBACK;
+		break;
+	case IFA_LINK:
+		retval = SCTP_SCOPE_LINK;
+		break;
+	case IFA_SITE:
+		retval = SCTP_SCOPE_PRIVATE;
+		break;
+	default:
+		retval = SCTP_SCOPE_GLOBAL;
+		break;
+	};
+
+	return retval;
+}
+
+/* Create and initialize a new sk for the socket to be returned by accept(). */
+struct sock *sctp_v6_create_accept_sk(struct sock *sk,
+				      struct sctp_association *asoc)
+{
+	struct sock *newsk;
+	struct inet_opt *newinet;
+	struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
+
+	newsk = sk_alloc(PF_INET6, GFP_KERNEL, sizeof(struct sock));
+	if (!newsk)
+		goto out;
+
+	sock_init_data(NULL, newsk);
+	sk_set_owner(newsk, THIS_MODULE);
+
+	newsk->type = SOCK_STREAM;
+
+	newsk->prot = sk->prot;
+	newsk->no_check = sk->no_check;
+	newsk->reuse = sk->reuse;
+
+	newsk->destruct = inet_sock_destruct;
+	newsk->zapped = 0;
+	newsk->family = PF_INET6;
+	newsk->protocol = IPPROTO_SCTP;
+	newsk->backlog_rcv = sk->prot->backlog_rcv;
+	newsk->shutdown = sk->shutdown;
+
+	newinet = inet_sk(newsk);
+	newnp = inet6_sk(newsk);
+
+	memcpy(newnp, np, sizeof(struct ipv6_pinfo));
+
+	/* Initialize sk's sport, dport, rcv_saddr and daddr for getsockname()
+	 * and getpeername().
+	 */
+	newsk->sport = sk->sport;
+	newnp->saddr = np->saddr;
+	newnp->rcv_saddr = np->rcv_saddr;
+	newsk->dport = htons(asoc->peer.port);
+	newnp->daddr =  asoc->peer.primary_addr.v6.sin6_addr;
+
+#ifdef INET_REFCNT_DEBUG
+	atomic_inc(&inet6_sock_nr);
+	atomic_inc(&inet_sock_nr);
+#endif
+
+	if (0 != newsk->prot->init(newsk)) {
+		inet_sock_release(newsk);
+		newsk = NULL;
+	}
+
+out:
+	return newsk;
+}
+
+/* Where did this skb come from?  */
+static int sctp_v6_skb_iif(const struct sk_buff *skb)
+{
+	struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb;
+	return opt->iif;
+}
+
+/* Initialize a PF_INET6 socket msg_name. */
+static void sctp_inet6_msgname(char *msgname, int *addr_len)
+{
+	struct sockaddr_in6 *sin6;
+
+	sin6 = (struct sockaddr_in6 *)msgname;
+	sin6->sin6_family = AF_INET6;
+	sin6->sin6_flowinfo = 0;
+	sin6->sin6_scope_id = 0; /*FIXME */
+	*addr_len = sizeof(struct sockaddr_in6);
+}
+
+/* Initialize a PF_INET msgname from a ulpevent. */
+static void sctp_inet6_event_msgname(struct sctp_ulpevent *event,
+				     char *msgname, int *addrlen)
+{
+	struct sockaddr_in6 *sin6, *sin6from;
+
+	if (msgname) {
+		union sctp_addr *addr;
+
+		sctp_inet6_msgname(msgname, addrlen);
+		sin6 = (struct sockaddr_in6 *)msgname;
+		sin6->sin6_port = htons(event->event_asoc->peer.port);
+		addr = &event->event_asoc->peer.primary_addr;
+
+		/* Note: If we go to a common v6 format, this code
+		 * will change.
+		 */
+
+		/* Map ipv4 address into v4-mapped-on-v6 address.  */
+		if (AF_INET == addr->sa.sa_family) {
+			/* FIXME: Easy, but there was no way to test this
+			 * yet.
+			 */
+			return;
+		}
+
+		sin6from = &event->event_asoc->peer.primary_addr.v6;
+		ipv6_addr_copy(&sin6->sin6_addr, &sin6from->sin6_addr);
+		if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)
+			sin6->sin6_scope_id = sin6from->sin6_scope_id;
+	}
+}
+
+/* Initialize a msg_name from an inbound skb. */
+static void sctp_inet6_skb_msgname(struct sk_buff *skb, char *msgname,
+				   int *addr_len)
+{
+	struct sctphdr *sh;
+	struct sockaddr_in6 *sin6;
+
+	if (msgname) {
+		sctp_inet6_msgname(msgname, addr_len);
+		sin6 = (struct sockaddr_in6 *)msgname;
+		sh = (struct sctphdr *)skb->h.raw;
+		sin6->sin6_port = sh->source;
+
+		/* FIXME: Map ipv4 address into v4-mapped-on-v6 address. */
+		if (__constant_htons(ETH_P_IP) == skb->protocol) {
+			/* FIXME:  The latest I-D added options for two
+			 * behaviors.
+			 */
+			return;
+		}
+
+		/* Otherwise, just copy the v6 address. */
+
+		ipv6_addr_copy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr);
+		if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) {
+			struct sctp_ulpevent *ev = sctp_skb2event(skb);
+			sin6->sin6_scope_id = ev->iif;
+		}
+	}
+}
+
+/* Do we support this AF? */
+static int sctp_inet6_af_supported(sa_family_t family)
+{
+	/* FIXME:  v4-mapped-v6 addresses.  The I-D is still waffling
+	 * on what to do with sockaddr formats for PF_INET6 sockets.
+	 * For now assume we'll support both.
+	 */
+	switch (family) {
+	case AF_INET6:
+	case AF_INET:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+/* Address matching with wildcards allowed.  This extra level
+ * of indirection lets us choose whether a PF_INET6 should
+ * disallow any v4 addresses if we so choose.
+ */
+static int sctp_inet6_cmp_addr(const union sctp_addr *addr1,
+			       const union sctp_addr *addr2,
+			       struct sctp_opt *opt)
+{
+	struct sctp_af *af1, *af2;
+
+	af1 = sctp_get_af_specific(addr1->sa.sa_family);
+	af2 = sctp_get_af_specific(addr2->sa.sa_family);
+
+	if (!af1 || !af2)
+		return 0;
+	/* Today, wildcard AF_INET/AF_INET6. */
+	if (sctp_is_any(addr1) || sctp_is_any(addr2))
+		return 1;
+
+	if (addr1->sa.sa_family != addr2->sa.sa_family)
+		return 0;
+
+	return af1->cmp_addr(addr1, addr2);
+}
+
+/* Verify that the provided sockaddr looks bindable.   Common verification,
+ * has already been taken care of.
+ */
+static int sctp_inet6_bind_verify(struct sctp_opt *opt, union sctp_addr *addr)
+{
+	struct sctp_af *af;
+
+	/* ASSERT: address family has already been verified. */
+	if (addr->sa.sa_family != AF_INET6)
+		af = sctp_get_af_specific(addr->sa.sa_family);
+	else {
+		struct sock *sk;
+		int type = ipv6_addr_type(&addr->v6.sin6_addr);
+		sk = container_of(opt, struct sock, tp_pinfo.af_sctp);
+		if (type & IPV6_ADDR_LINKLOCAL) {
+			/* Note: Behavior similar to af_inet6.c:
+			 *  1) Overrides previous bound_dev_if
+			 *  2) Destructive even if bind isn't successful.
+			 */
+
+			if (addr->v6.sin6_scope_id)
+				sk->bound_dev_if = addr->v6.sin6_scope_id;
+			if (!sk->bound_dev_if)
+				return 0;
+		}
+		af = opt->pf->af;
+	}
+	return af->available(addr);
+}
+
+/* Verify that the provided sockaddr looks bindable.   Common verification,
+ * has already been taken care of.
+ */
+static int sctp_inet6_send_verify(struct sctp_opt *opt, union sctp_addr *addr)
+{
+	struct sctp_af *af = NULL;
+
+	/* ASSERT: address family has already been verified. */
+	if (addr->sa.sa_family != AF_INET6)
+		af = sctp_get_af_specific(addr->sa.sa_family);
+	else {
+		struct sock *sk;
+		int type = ipv6_addr_type(&addr->v6.sin6_addr);
+		sk = container_of(opt, struct sock, tp_pinfo.af_sctp);
+		if (type & IPV6_ADDR_LINKLOCAL) {
+			/* Note: Behavior similar to af_inet6.c:
+			 *  1) Overrides previous bound_dev_if
+			 *  2) Destructive even if bind isn't successful.
+			 */
+
+			if (addr->v6.sin6_scope_id)
+				sk->bound_dev_if = addr->v6.sin6_scope_id;
+			if (!sk->bound_dev_if)
+				return 0;
+		}
+		af = opt->pf->af;
+	}
+
+	return af != NULL;
+}
+
+/* Fill in Supported Address Type information for INIT and INIT-ACK
+ * chunks.   Note: In the future, we may want to look at sock options
+ * to determine whether a PF_INET6 socket really wants to have IPV4
+ * addresses.
+ * Returns number of addresses supported.
+ */
+static int sctp_inet6_supported_addrs(const struct sctp_opt *opt,
+				      __u16 *types)
+{
+	types[0] = SCTP_PARAM_IPV4_ADDRESS;
+	types[1] = SCTP_PARAM_IPV6_ADDRESS;
+	return 2;
+}
+
+static struct proto_ops inet6_seqpacket_ops = {
+	.family     = PF_INET6,
+	.release    = inet6_release,
+	.bind       = inet6_bind,
+	.connect    = inet_dgram_connect,
+	.socketpair = sock_no_socketpair,
+	.accept     = inet_accept,
+	.getname    = inet6_getname,
+	.poll       = sctp_poll,
+	.ioctl      = inet6_ioctl,
+	.listen     = sctp_inet_listen,
+	.shutdown   = inet_shutdown,
+	.setsockopt = inet_setsockopt,
+	.getsockopt = inet_getsockopt,
+	.sendmsg    = inet_sendmsg,
+	.recvmsg    = inet_recvmsg,
+	.mmap       = sock_no_mmap,
+};
+
+static struct inet_protosw sctpv6_seqpacket_protosw = {
+	.type          = SOCK_SEQPACKET,
+	.protocol      = IPPROTO_SCTP,
+	.prot 	       = &sctp_prot,
+	.ops           = &inet6_seqpacket_ops,
+	.capability    = -1,
+	.no_check      = 0,
+	.flags         = SCTP_PROTOSW_FLAG
+};
+static struct inet_protosw sctpv6_stream_protosw = {
+	.type          = SOCK_STREAM,
+	.protocol      = IPPROTO_SCTP,
+	.prot 	       = &sctp_prot,
+	.ops           = &inet6_seqpacket_ops,
+	.capability    = -1,
+	.no_check      = 0,
+	.flags         = SCTP_PROTOSW_FLAG,
+};
+
+static struct inet6_protocol sctpv6_protocol = {
+	.handler      = sctp_rcv,
+	.err_handler  = sctp_v6_err,
+	.next         = NULL,
+	.protocol     = IPPROTO_SCTP,
+	.copy         = 0,
+	.data         = NULL,
+	.name         = "SCTPv6",
+};
+
+static struct sctp_af sctp_ipv6_specific = {
+	.sctp_xmit       = sctp_v6_xmit,
+	.setsockopt      = ipv6_setsockopt,
+	.getsockopt      = ipv6_getsockopt,
+	.get_dst	 = sctp_v6_get_dst,
+	.get_saddr	 = sctp_v6_get_saddr,
+	.copy_addrlist   = sctp_v6_copy_addrlist,
+	.from_skb        = sctp_v6_from_skb,
+	.from_sk         = sctp_v6_from_sk,
+	.to_sk_saddr     = sctp_v6_to_sk_saddr,
+	.to_sk_daddr     = sctp_v6_to_sk_daddr,
+	.dst_saddr       = sctp_v6_dst_saddr,
+	.cmp_addr        = sctp_v6_cmp_addr,
+	.scope           = sctp_v6_scope,
+	.addr_valid      = sctp_v6_addr_valid,
+	.inaddr_any      = sctp_v6_inaddr_any,
+	.is_any          = sctp_v6_is_any,
+	.available       = sctp_v6_available,
+	.skb_iif         = sctp_v6_skb_iif,
+	.net_header_len  = sizeof(struct ipv6hdr),
+	.sockaddr_len    = sizeof(struct sockaddr_in6),
+	.sa_family       = AF_INET6,
+};
+
+static struct sctp_pf sctp_pf_inet6_specific = {
+	.event_msgname = sctp_inet6_event_msgname,
+	.skb_msgname   = sctp_inet6_skb_msgname,
+	.af_supported  = sctp_inet6_af_supported,
+	.cmp_addr      = sctp_inet6_cmp_addr,
+	.bind_verify   = sctp_inet6_bind_verify,
+	.send_verify   = sctp_inet6_send_verify,
+	.supported_addrs = sctp_inet6_supported_addrs,
+	.create_accept_sk = sctp_v6_create_accept_sk,
+	.af            = &sctp_ipv6_specific,
+};
+
+/* Initialize IPv6 support and register with inet6 stack.  */
+int sctp_v6_init(void)
+{
+	/* Register inet6 protocol. */
+	inet6_add_protocol(&sctpv6_protocol);
+
+	/* Add SCTPv6(UDP and TCP style) to inetsw6 linked list. */
+	inet6_register_protosw(&sctpv6_seqpacket_protosw);
+	inet6_register_protosw(&sctpv6_stream_protosw);
+
+	/* Register the SCTP specific PF_INET6 functions. */
+	sctp_register_pf(&sctp_pf_inet6_specific, PF_INET6);
+
+	/* Register the SCTP specific AF_INET6 functions. */
+	sctp_register_af(&sctp_ipv6_specific);
+
+	/* Register notifier for inet6 address additions/deletions. */
+	register_inet6addr_notifier(&sctp_inetaddr_notifier);
+
+	return 0;
+}
+
+/* IPv6 specific exit support. */
+void sctp_v6_exit(void)
+{
+	list_del(&sctp_ipv6_specific.list);
+	inet6_del_protocol(&sctpv6_protocol);
+	inet6_unregister_protosw(&sctpv6_seqpacket_protosw);
+	inet6_unregister_protosw(&sctpv6_stream_protosw);
+	unregister_inet6addr_notifier(&sctp_inetaddr_notifier);
+}
diff -urN linux-2.4.23-pre5/net/sctp/objcnt.c linux-2.4.23-pre6/net/sctp/objcnt.c
--- linux-2.4.23-pre5/net/sctp/objcnt.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/objcnt.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,135 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 2001 International Business Machines Corp.
+ * 
+ * This file is part of the SCTP kernel reference Implementation
+ * 
+ * Support for memory object debugging.  This allows one to monitor the
+ * object allocations/deallocations for types instrumented for this 
+ * via the proc fs. 
+ * 
+ * The SCTP reference implementation is free software; 
+ * you can redistribute it and/or modify it under the terms of 
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ * 
+ * The SCTP reference implementation is distributed in the hope that it 
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.  
+ * 
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ * 
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by: 
+ *    Jon Grimm             <jgrimm@us.ibm.com>
+ * 
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <net/sctp/sctp.h>
+
+/*
+ * Global counters to count raw object allocation counts.
+ * To add new counters, choose a unique suffix for the variable
+ * name as the helper macros key off this suffix to make
+ * life easier for the programmer.
+ */
+
+SCTP_DBG_OBJCNT(sock);
+SCTP_DBG_OBJCNT(ep);
+SCTP_DBG_OBJCNT(transport);
+SCTP_DBG_OBJCNT(assoc);
+SCTP_DBG_OBJCNT(bind_addr);
+SCTP_DBG_OBJCNT(chunk);
+SCTP_DBG_OBJCNT(addr);
+SCTP_DBG_OBJCNT(ssnmap);
+
+/* An array to make it easy to pretty print the debug information
+ * to the proc fs.
+ */
+sctp_dbg_objcnt_entry_t sctp_dbg_objcnt[] = {
+	SCTP_DBG_OBJCNT_ENTRY(sock),
+	SCTP_DBG_OBJCNT_ENTRY(ep),
+	SCTP_DBG_OBJCNT_ENTRY(assoc),
+	SCTP_DBG_OBJCNT_ENTRY(transport),
+	SCTP_DBG_OBJCNT_ENTRY(chunk),
+	SCTP_DBG_OBJCNT_ENTRY(bind_addr),
+	SCTP_DBG_OBJCNT_ENTRY(addr),
+	SCTP_DBG_OBJCNT_ENTRY(ssnmap),
+};
+
+/* Callback from procfs to read out objcount information.
+ * Walk through the entries in the sctp_dbg_objcnt array, dumping
+ * the raw object counts for each monitored type.
+ *
+ * This code was modified from similar code in route.c
+ */
+static int sctp_dbg_objcnt_read(char *buffer, char **start, off_t offset,
+				int length, int *eof, void *data)
+{
+	int len = 0;
+	off_t pos = 0;
+	int entries;
+	int i;
+	char temp[128];
+
+	/* How many entries? */
+	entries = sizeof(sctp_dbg_objcnt)/sizeof(sctp_dbg_objcnt[0]);
+
+	/* Walk the entries and print out the debug information
+	 * for proc fs.
+	 */
+	for (i = 0; i < entries; i++) {
+		pos += 128;
+
+		/* Skip ahead. */
+		if (pos <= offset) {
+			len = 0;
+			continue;
+		}
+		/* Print out each entry. */
+		sprintf(temp, "%s: %d",
+			sctp_dbg_objcnt[i].label,
+			atomic_read(sctp_dbg_objcnt[i].counter));
+
+		sprintf(buffer + len, "%-127s\n", temp);
+		len += 128;
+		if (pos >= offset+length)
+			goto done;
+	}
+
+done:
+	*start = buffer + len - (pos - offset);
+	len = pos - offset;
+	if (len > length)
+		len = length;
+
+  	return len;
+}
+
+/* Initialize the objcount in the proc filesystem.  */
+void sctp_dbg_objcnt_init(void)
+{
+	create_proc_read_entry("sctp_dbg_objcnt", 0, proc_net_sctp,
+			       sctp_dbg_objcnt_read, NULL);
+}
+
+/* Cleanup the objcount entry in the proc filesystem.  */
+void sctp_dbg_objcnt_exit(void)
+{
+	remove_proc_entry("sctp_dbg_objcount", proc_net_sctp);
+}
+
+
diff -urN linux-2.4.23-pre5/net/sctp/output.c linux-2.4.23-pre6/net/sctp/output.c
--- linux-2.4.23-pre5/net/sctp/output.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/output.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,636 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001-2003 International Business Machines, Corp.
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * These functions handle output processing.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Karl Knutson          <karl@athena.chicago.il.us>
+ *    Jon Grimm             <jgrimm@austin.ibm.com>
+ *    Sridhar Samudrala     <sri@us.ibm.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/init.h>
+#include <net/inet_ecn.h>
+#include <net/icmp.h>
+
+#ifndef TEST_FRAME
+#include <net/tcp.h>
+#endif /* TEST_FRAME (not defined) */
+
+#include <linux/socket.h> /* for sa_family_t */
+#include <net/sock.h>
+
+#include <net/sctp/sctp.h>
+#include <net/sctp/sm.h>
+
+/* Forward declarations for private helpers. */
+static void sctp_packet_reset(struct sctp_packet *packet);
+static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet,
+					   struct sctp_chunk *chunk);
+
+/* Config a packet.
+ * This appears to be a followup set of initializations.)
+ */
+struct sctp_packet *sctp_packet_config(struct sctp_packet *packet,
+				       __u32 vtag, int ecn_capable,
+				       sctp_packet_phandler_t *prepend_handler)
+{
+	int packet_empty = (packet->size == SCTP_IP_OVERHEAD);
+
+	packet->vtag = vtag;
+	packet->ecn_capable = ecn_capable;
+	packet->get_prepend_chunk = prepend_handler;
+	packet->has_cookie_echo = 0;
+	packet->has_sack = 0;
+	packet->ipfragok = 0;
+
+	/* We might need to call the prepend_handler right away.  */
+	if (packet_empty)
+		sctp_packet_reset(packet);
+	return packet;
+}
+
+/* Initialize the packet structure. */
+struct sctp_packet *sctp_packet_init(struct sctp_packet *packet,
+				     struct sctp_transport *transport,
+				     __u16 sport, __u16 dport)
+{
+	packet->transport = transport;
+	packet->source_port = sport;
+	packet->destination_port = dport;
+	skb_queue_head_init(&packet->chunks);
+	packet->vtag = 0;
+	packet->ecn_capable = 0;
+	packet->get_prepend_chunk = NULL;
+	packet->has_cookie_echo = 0;
+	packet->has_sack = 0;
+	packet->ipfragok = 0;
+	packet->malloced = 0;
+	sctp_packet_reset(packet);
+	return packet;
+}
+
+/* Free a packet.  */
+void sctp_packet_free(struct sctp_packet *packet)
+{
+	struct sctp_chunk *chunk;
+
+        while ((chunk = (struct sctp_chunk *)__skb_dequeue(&packet->chunks)))
+		sctp_free_chunk(chunk);
+
+	if (packet->malloced)
+		kfree(packet);
+}
+
+/* This routine tries to append the chunk to the offered packet. If adding
+ * the chunk causes the packet to exceed the path MTU and COOKIE_ECHO chunk
+ * is not present in the packet, it transmits the input packet.
+ * Data can be bundled with a packet containing a COOKIE_ECHO chunk as long
+ * as it can fit in the packet, but any more data that does not fit in this
+ * packet can be sent only after receiving the COOKIE_ACK.
+ */
+sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet,
+				       struct sctp_chunk *chunk)
+{
+	sctp_xmit_t retval;
+	int error = 0;
+
+	switch ((retval = (sctp_packet_append_chunk(packet, chunk)))) {
+	case SCTP_XMIT_PMTU_FULL:
+		if (!packet->has_cookie_echo) {
+			error = sctp_packet_transmit(packet);
+			if (error < 0)
+				chunk->skb->sk->err = -error;
+
+			/* If we have an empty packet, then we can NOT ever
+			 * return PMTU_FULL.
+			 */
+			retval = sctp_packet_append_chunk(packet, chunk);
+		}
+		break;
+
+	case SCTP_XMIT_MUST_FRAG:
+	case SCTP_XMIT_RWND_FULL:
+	case SCTP_XMIT_OK:
+	case SCTP_XMIT_NAGLE_DELAY:
+		break;
+	};
+
+	return retval;
+}
+
+/* Try to bundle a SACK with the packet. */
+static sctp_xmit_t sctp_packet_bundle_sack(struct sctp_packet *pkt,
+					   struct sctp_chunk *chunk)
+{
+	sctp_xmit_t retval = SCTP_XMIT_OK;
+
+	/* If sending DATA and haven't aleady bundled a SACK, try to
+	 * bundle one in to the packet.
+	 */
+	if (sctp_chunk_is_data(chunk) && !pkt->has_sack &&
+	    !pkt->has_cookie_echo) {
+		struct sctp_association *asoc;
+		asoc = pkt->transport->asoc;
+
+		if (asoc->a_rwnd > asoc->rwnd) {
+			struct sctp_chunk *sack;
+			asoc->a_rwnd = asoc->rwnd;
+			sack = sctp_make_sack(asoc);
+			if (sack) {
+				struct timer_list *timer;
+				retval = sctp_packet_append_chunk(pkt, sack);
+				asoc->peer.sack_needed = 0;
+				timer = &asoc->timers[SCTP_EVENT_TIMEOUT_SACK];
+				if (timer_pending(timer) && del_timer(timer))
+					sctp_association_put(asoc);
+			}
+		}
+	}
+	return retval;
+}
+
+/* Append a chunk to the offered packet reporting back any inability to do
+ * so.
+ */
+sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet,
+				     struct sctp_chunk *chunk)
+{
+	sctp_xmit_t retval = SCTP_XMIT_OK;
+	__u16 chunk_len = WORD_ROUND(ntohs(chunk->chunk_hdr->length));
+	size_t psize;
+	size_t pmtu;
+	int too_big;
+
+	retval = sctp_packet_bundle_sack(packet, chunk);
+	psize = packet->size;
+
+	if (retval != SCTP_XMIT_OK)
+		goto finish;
+
+	pmtu  = ((packet->transport->asoc) ?
+		 (packet->transport->asoc->pmtu) :
+		 (packet->transport->pmtu));
+
+	too_big = (psize + chunk_len > pmtu);
+
+	/* Decide if we need to fragment or resubmit later. */
+	if (too_big) {
+		int packet_empty = (packet->size == SCTP_IP_OVERHEAD);
+
+		/* Both control chunks and data chunks with TSNs are
+		 * non-fragmentable.
+		 */
+		int fragmentable = sctp_chunk_is_data(chunk) &&
+			(!chunk->has_tsn);
+		if (packet_empty) {
+			if (fragmentable) {
+				retval = SCTP_XMIT_MUST_FRAG;
+				goto finish;
+			} else {
+				/* The packet is too big but we can
+				 * not fragment it--we have to just
+				 * transmit and rely on IP
+				 * fragmentation.
+				 */
+				packet->ipfragok = 1;
+				goto append;
+			}
+		} else { /* !packet_empty */
+			retval = SCTP_XMIT_PMTU_FULL;
+			goto finish;
+		}
+	} else {
+		/* The chunk fits in the packet.  */
+		goto append;
+	}
+
+append:
+	/* We believe that this chunk is OK to add to the packet (as
+	 * long as we have the cwnd for it).
+	 */
+
+	/* DATA is a special case since we must examine both rwnd and cwnd
+	 * before we send DATA.
+	 */
+	if (sctp_chunk_is_data(chunk)) {
+		retval = sctp_packet_append_data(packet, chunk);
+		/* Disallow SACK bundling after DATA. */
+		packet->has_sack = 1;
+		if (SCTP_XMIT_OK != retval)
+			goto finish;
+	} else if (SCTP_CID_COOKIE_ECHO == chunk->chunk_hdr->type)
+		packet->has_cookie_echo = 1;
+	else if (SCTP_CID_SACK == chunk->chunk_hdr->type)
+		packet->has_sack = 1;
+
+	/* It is OK to send this chunk.  */
+	__skb_queue_tail(&packet->chunks, (struct sk_buff *)chunk);
+	packet->size += chunk_len;
+finish:
+	return retval;
+}
+
+/* All packets are sent to the network through this function from
+ * sctp_outq_tail().
+ *
+ * The return value is a normal kernel error return value.
+ */
+int sctp_packet_transmit(struct sctp_packet *packet)
+{
+	struct sctp_transport *tp = packet->transport;
+	struct sctp_association *asoc = tp->asoc;
+	struct sctphdr *sh;
+	__u32 crc32;
+	struct sk_buff *nskb;
+	struct sctp_chunk *chunk;
+	struct sock *sk;
+	int err = 0;
+	int padding;		/* How much padding do we need?  */
+	__u8 has_data = 0;
+	struct dst_entry *dst;
+
+	/* Do NOT generate a chunkless packet... */
+	if (skb_queue_empty(&packet->chunks))
+		return err;
+
+	/* Set up convenience variables... */
+	chunk = (struct sctp_chunk *) (packet->chunks.next);
+	sk = chunk->skb->sk;
+
+	/* Allocate the new skb.  */
+	nskb = dev_alloc_skb(packet->size);
+	if (!nskb) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	/* Make sure the outbound skb has enough header room reserved. */
+	skb_reserve(nskb, SCTP_IP_OVERHEAD);
+
+	/* Set the owning socket so that we know where to get the
+	 * destination IP address.
+	 */
+	skb_set_owner_w(nskb, sk);
+
+	/* Build the SCTP header.  */
+	sh = (struct sctphdr *)skb_push(nskb, sizeof(struct sctphdr));
+	sh->source = htons(packet->source_port);
+	sh->dest   = htons(packet->destination_port);
+
+	/* From 6.8 Adler-32 Checksum Calculation:
+	 * After the packet is constructed (containing the SCTP common
+	 * header and one or more control or DATA chunks), the
+	 * transmitter shall:
+	 *
+	 * 1) Fill in the proper Verification Tag in the SCTP common
+	 *    header and initialize the checksum field to 0's.
+	 */
+	sh->vtag     = htonl(packet->vtag);
+	sh->checksum = 0;
+
+
+	/* 2) Calculate the Adler-32 checksum of the whole packet,
+	 *    including the SCTP common header and all the
+	 *    chunks.
+	 *
+	 * Note: Adler-32 is no longer applicable, as has been replaced
+	 * by CRC32-C as described in <draft-ietf-tsvwg-sctpcsum-02.txt>.
+	 */
+	crc32 = sctp_start_cksum((__u8 *)sh, sizeof(struct sctphdr));
+
+	/**
+	 * 6.10 Bundling
+	 *
+	 *    An endpoint bundles chunks by simply including multiple
+	 *    chunks in one outbound SCTP packet.  ...
+	 */
+
+	/**
+	 * 3.2  Chunk Field Descriptions
+	 *
+	 * The total length of a chunk (including Type, Length and
+	 * Value fields) MUST be a multiple of 4 bytes.  If the length
+	 * of the chunk is not a multiple of 4 bytes, the sender MUST
+	 * pad the chunk with all zero bytes and this padding is not
+	 * included in the chunk length field.  The sender should
+	 * never pad with more than 3 bytes.
+	 *
+	 * [This whole comment explains WORD_ROUND() below.]
+	 */
+	SCTP_DEBUG_PRINTK("***sctp_transmit_packet***\n");
+	while ((chunk = (struct sctp_chunk *)__skb_dequeue(&packet->chunks))) {
+		chunk->num_times_sent++;
+		chunk->sent_at = jiffies;
+		if (sctp_chunk_is_data(chunk)) {
+			sctp_chunk_assign_tsn(chunk);
+
+			/* 6.3.1 C4) When data is in flight and when allowed
+			 * by rule C5, a new RTT measurement MUST be made each
+			 * round trip.  Furthermore, new RTT measurements
+			 * SHOULD be made no more than once per round-trip
+			 * for a given destination transport address.
+			 */
+			if ((1 == chunk->num_times_sent) &&
+			    (!tp->rto_pending)) {
+				chunk->rtt_in_progress = 1;
+				tp->rto_pending = 1;
+			}
+			has_data = 1;
+		}
+		padding = WORD_ROUND(chunk->skb->len) - chunk->skb->len;
+		if (padding)
+			memset(skb_put(chunk->skb, padding), 0, padding);      
+
+		crc32 = sctp_update_copy_cksum(skb_put(nskb, chunk->skb->len),
+					       chunk->skb->data, 
+					       chunk->skb->len, crc32);
+		
+		SCTP_DEBUG_PRINTK("%s %p[%s] %s 0x%x, %s %d, %s %d, %s %d, "
+				  "%s %d\n",
+				  "*** Chunk", chunk,
+				  sctp_cname(SCTP_ST_CHUNK(
+					  chunk->chunk_hdr->type)),
+				  chunk->has_tsn ? "TSN" : "No TSN",
+				  chunk->has_tsn ?
+				  ntohl(chunk->subh.data_hdr->tsn) : 0,
+				  "length", ntohs(chunk->chunk_hdr->length),
+				  "chunk->skb->len", chunk->skb->len,
+				  "num_times_sent", chunk->num_times_sent,
+				  "rtt_in_progress", chunk->rtt_in_progress);
+
+		/*
+		 * If this is a control chunk, this is our last
+		 * reference. Free data chunks after they've been
+		 * acknowledged or have failed.
+		 */
+		if (!sctp_chunk_is_data(chunk))
+			sctp_free_chunk(chunk);
+	}
+
+	/* Perform final transformation on checksum. */
+	crc32 = sctp_end_cksum(crc32);
+
+	/* 3) Put the resultant value into the checksum field in the
+	 *    common header, and leave the rest of the bits unchanged.
+	 */
+	sh->checksum = htonl(crc32);
+
+	/* IP layer ECN support
+	 * From RFC 2481
+	 *  "The ECN-Capable Transport (ECT) bit would be set by the
+	 *   data sender to indicate that the end-points of the
+	 *   transport protocol are ECN-capable."
+	 *
+	 * If ECN capable && negotiated && it makes sense for
+	 * this packet to support it (e.g. post ECN negotiation)
+	 * then lets set the ECT bit
+	 *
+	 * FIXME:  Need to do something else for IPv6
+	 */
+	if (packet->ecn_capable) {
+		INET_ECN_xmit(nskb->sk);
+	} else {
+		INET_ECN_dontxmit(nskb->sk);
+	}
+
+	/* Set up the IP options.  */
+	/* BUG: not implemented
+	 * For v4 this all lives somewhere in sk->opt...
+	 */
+
+	/* Dump that on IP!  */
+	if (asoc && asoc->peer.last_sent_to != tp) {
+		/* Considering the multiple CPU scenario, this is a
+		 * "correcter" place for last_sent_to.  --xguo
+		 */
+		asoc->peer.last_sent_to = tp;
+	}
+
+	if (has_data) {
+		struct timer_list *timer;
+		unsigned long timeout;
+
+		tp->last_time_used = jiffies;
+
+		/* Restart the AUTOCLOSE timer when sending data. */
+		if ((SCTP_STATE_ESTABLISHED == asoc->state) &&
+		    (asoc->autoclose)) {
+			timer = &asoc->timers[SCTP_EVENT_TIMEOUT_AUTOCLOSE];
+			timeout = asoc->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE];
+
+			if (!mod_timer(timer, jiffies + timeout))
+				sctp_association_hold(asoc);
+		}
+	}
+
+	dst = tp->dst;
+	/* The 'obsolete' field of dst is set to 2 when a dst is freed. */
+	if (!dst || (dst->obsolete > 1)) {
+		dst_release(dst);
+		sctp_transport_route(tp, NULL, sctp_sk(sk));
+		sctp_assoc_sync_pmtu(asoc);
+	}
+
+	nskb->dst = dst_clone(tp->dst);
+	if (!nskb->dst)
+		goto no_route;
+
+	SCTP_DEBUG_PRINTK("***sctp_transmit_packet*** skb length %d\n",
+			  nskb->len);
+	(*tp->af_specific->sctp_xmit)(nskb, tp, packet->ipfragok);
+out:
+	packet->size = SCTP_IP_OVERHEAD;
+	return err;
+no_route:
+	kfree_skb(nskb);
+	IP_INC_STATS_BH(IpOutNoRoutes);
+
+	/* FIXME: Returning the 'err' will effect all the associations
+	 * associated with a socket, although only one of the paths of the
+	 * association is unreachable.
+	 * The real failure of a transport or association can be passed on
+	 * to the user via notifications. So setting this error may not be
+	 * required.
+	 */
+	 /* err = -EHOSTUNREACH; */
+	goto out;
+}
+
+/********************************************************************
+ * 2nd Level Abstractions
+ ********************************************************************/
+
+/*
+ * This private function resets the packet to a fresh state.
+ */
+static void sctp_packet_reset(struct sctp_packet *packet)
+{
+	struct sctp_chunk *chunk = NULL;
+
+	packet->size = SCTP_IP_OVERHEAD;
+
+	if (packet->get_prepend_chunk)
+		chunk = packet->get_prepend_chunk(packet->transport->asoc);
+
+	/* If there a is a prepend chunk stick it on the list before
+	 * any other chunks get appended.
+	 */
+	if (chunk)
+		sctp_packet_append_chunk(packet, chunk);
+}
+
+/* This private function handles the specifics of appending DATA chunks.  */
+static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet,
+					   struct sctp_chunk *chunk)
+{
+	sctp_xmit_t retval = SCTP_XMIT_OK;
+	size_t datasize, rwnd, inflight;
+	struct sctp_transport *transport = packet->transport;
+	__u32 max_burst_bytes;
+	struct sctp_association *asoc = transport->asoc;
+	struct sctp_opt *sp = sctp_sk(asoc->base.sk);
+	struct sctp_outq *q = &asoc->outqueue;
+
+	/* RFC 2960 6.1  Transmission of DATA Chunks
+	 *
+	 * A) At any given time, the data sender MUST NOT transmit new data to
+	 * any destination transport address if its peer's rwnd indicates
+	 * that the peer has no buffer space (i.e. rwnd is 0, see Section
+	 * 6.2.1).  However, regardless of the value of rwnd (including if it
+	 * is 0), the data sender can always have one DATA chunk in flight to
+	 * the receiver if allowed by cwnd (see rule B below).  This rule
+	 * allows the sender to probe for a change in rwnd that the sender
+	 * missed due to the SACK having been lost in transit from the data
+	 * receiver to the data sender.
+	 */
+
+	rwnd = asoc->peer.rwnd;
+	inflight = asoc->outqueue.outstanding_bytes;
+
+	datasize = sctp_data_size(chunk);
+
+	if (datasize > rwnd) {
+		if (inflight > 0) {
+			/* We have (at least) one data chunk in flight,
+			 * so we can't fall back to rule 6.1 B).
+			 */
+			retval = SCTP_XMIT_RWND_FULL;
+			goto finish;
+		}
+	}
+
+	/* sctpimpguide-05 2.14.2
+	 * D) When the time comes for the sender to
+	 * transmit new DATA chunks, the protocol parameter Max.Burst MUST
+	 * first be applied to limit how many new DATA chunks may be sent.
+	 * The limit is applied by adjusting cwnd as follows:
+	 * 	if ((flightsize + Max.Burst * MTU) < cwnd)
+	 *		cwnd = flightsize + Max.Burst * MTU
+	 */
+	max_burst_bytes = asoc->max_burst * asoc->pmtu;
+	if ((transport->flight_size + max_burst_bytes) < transport->cwnd) {
+		transport->cwnd = transport->flight_size + max_burst_bytes;
+		SCTP_DEBUG_PRINTK("%s: cwnd limited by max_burst: "
+				  "transport: %p, cwnd: %d, "
+				  "ssthresh: %d, flight_size: %d, "
+				  "pba: %d\n",
+				  __FUNCTION__, transport,
+				  transport->cwnd,
+				  transport->ssthresh,
+				  transport->flight_size,
+				  transport->partial_bytes_acked);
+	}
+
+	/* RFC 2960 6.1  Transmission of DATA Chunks
+	 *
+	 * B) At any given time, the sender MUST NOT transmit new data
+	 * to a given transport address if it has cwnd or more bytes
+	 * of data outstanding to that transport address.
+	 */
+	/* RFC 7.2.4 & the Implementers Guide 2.8.
+	 *
+	 * 3) ...
+	 *    When a Fast Retransmit is being performed the sender SHOULD
+	 *    ignore the value of cwnd and SHOULD NOT delay retransmission.
+	 */
+	if (!chunk->fast_retransmit)
+		if (transport->flight_size >= transport->cwnd) {
+			retval = SCTP_XMIT_RWND_FULL;
+			goto finish;
+		}
+
+	/* Nagle's algorithm to solve small-packet problem:
+	 * Inhibit the sending of new chunks when new outgoing data arrives
+	 * if any previously transmitted data on the connection remains
+	 * unacknowledged.
+	 */
+	if (!sp->nodelay && SCTP_IP_OVERHEAD == packet->size &&
+	    q->outstanding_bytes && SCTP_STATE_ESTABLISHED == asoc->state) {
+		unsigned len = datasize + q->out_qlen;
+
+		/* Check whether this chunk and all the rest of pending
+		 * data will fit or delay in hopes of bundling a full
+		 * sized packet.
+		 */
+		if (len < asoc->pmtu - SCTP_IP_OVERHEAD) {
+			retval = SCTP_XMIT_NAGLE_DELAY;
+			goto finish;
+		}
+	}
+
+	/* Keep track of how many bytes are in flight over this transport. */
+	transport->flight_size += datasize;
+
+	/* Keep track of how many bytes are in flight to the receiver. */
+	asoc->outqueue.outstanding_bytes += datasize;
+
+	/* Update our view of the receiver's rwnd. */
+	if (datasize < rwnd)
+		rwnd -= datasize;
+	else
+		rwnd = 0;
+
+	asoc->peer.rwnd = rwnd;
+
+finish:
+	return retval;
+}
diff -urN linux-2.4.23-pre5/net/sctp/outqueue.c linux-2.4.23-pre6/net/sctp/outqueue.c
--- linux-2.4.23-pre5/net/sctp/outqueue.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/outqueue.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,1637 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001-2003 Intel Corp.
+ * Copyright (c) 2001-2003 International Business Machines Corp.
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * These functions implement the sctp_outq class.   The outqueue handles
+ * bundling and queueing of outgoing SCTP chunks.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Karl Knutson          <karl@athena.chicago.il.us>
+ *    Perry Melange         <pmelange@null.cc.uic.edu>
+ *    Xingang Guo           <xingang.guo@intel.com>
+ *    Hui Huang 	    <hui.huang@nokia.com>
+ *    Sridhar Samudrala     <sri@us.ibm.com>
+ *    Jon Grimm             <jgrimm@us.ibm.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/types.h>
+#include <linux/list.h>   /* For struct list_head */
+#include <linux/socket.h>
+#include <linux/ip.h>
+#include <net/sock.h>	  /* For skb_set_owner_w */
+
+#include <net/sctp/sctp.h>
+
+/* Declare internal functions here.  */
+static int sctp_acked(sctp_sackhdr_t *sack, __u32 tsn);
+static void sctp_check_transmitted(struct sctp_outq *q,
+				   struct list_head *transmitted_queue,
+				   struct sctp_transport *transport,
+				   sctp_sackhdr_t *sack,
+				   __u32 highest_new_tsn);
+
+/* Add data to the front of the queue. */
+static inline void sctp_outq_head_data(struct sctp_outq *q,
+					struct sctp_chunk *ch)
+{
+	__skb_queue_head(&q->out, (struct sk_buff *)ch);
+	q->out_qlen += ch->skb->len;
+	return;
+}
+
+/* Take data from the front of the queue. */
+static inline struct sctp_chunk *sctp_outq_dequeue_data(struct sctp_outq *q)
+{
+	struct sctp_chunk *ch;
+	ch = (struct sctp_chunk *)__skb_dequeue(&q->out);
+	if (ch)
+		q->out_qlen -= ch->skb->len;
+	return ch;
+}
+/* Add data chunk to the end of the queue. */
+static inline void sctp_outq_tail_data(struct sctp_outq *q,
+				       struct sctp_chunk *ch)
+{
+	__skb_queue_tail(&q->out, (struct sk_buff *)ch);
+	q->out_qlen += ch->skb->len;
+	return;
+}
+
+/* Insert a chunk behind chunk 'pos'. */
+static inline void sctp_outq_insert_data(struct sctp_outq *q,
+					 struct sctp_chunk *ch,
+					 struct sctp_chunk *pos)
+{
+	__skb_insert((struct sk_buff *)ch, (struct sk_buff *)pos->prev,
+		     (struct sk_buff *)pos, pos->list);
+	q->out_qlen += ch->skb->len;
+}
+
+/* Generate a new outqueue.  */
+struct sctp_outq *sctp_outq_new(struct sctp_association *asoc)
+{
+	struct sctp_outq *q;
+
+	q = t_new(struct sctp_outq, GFP_KERNEL);
+	if (q) {
+		sctp_outq_init(asoc, q);
+		q->malloced = 1;
+	}
+	return q;
+}
+
+/* Initialize an existing sctp_outq.  This does the boring stuff.
+ * You still need to define handlers if you really want to DO
+ * something with this structure...
+ */
+void sctp_outq_init(struct sctp_association *asoc, struct sctp_outq *q)
+{
+	q->asoc = asoc;
+	skb_queue_head_init(&q->out);
+	skb_queue_head_init(&q->control);
+	INIT_LIST_HEAD(&q->retransmit);
+	INIT_LIST_HEAD(&q->sacked);
+
+	q->init_output = NULL;
+	q->config_output = NULL;
+	q->append_output = NULL;
+	q->build_output = NULL;
+	q->force_output = NULL;
+
+	q->outstanding_bytes = 0;
+	q->empty = 1;
+
+	q->malloced = 0;
+	q->out_qlen = 0;
+}
+
+/* Free the outqueue structure and any related pending chunks.
+ */
+void sctp_outq_teardown(struct sctp_outq *q)
+{
+	struct sctp_transport *transport;
+	struct list_head *lchunk, *pos, *temp;
+	sctp_chunk_t *chunk;
+	struct sctp_ulpevent *ev;
+
+	/* Throw away unacknowledged chunks. */
+	list_for_each(pos, &q->asoc->peer.transport_addr_list) {
+		transport = list_entry(pos, struct sctp_transport, transports);
+		while ((lchunk = sctp_list_dequeue(&transport->transmitted))) {
+			chunk = list_entry(lchunk, sctp_chunk_t,
+					   transmitted_list);
+
+			/* Generate a SEND FAILED event. */
+			ev = sctp_ulpevent_make_send_failed(q->asoc,
+					chunk, SCTP_DATA_SENT,
+					q->error, GFP_ATOMIC);
+			if (ev)
+				sctp_ulpq_tail_event(&q->asoc->ulpq, ev);
+
+			sctp_free_chunk(chunk);
+		}
+	}
+
+	/* Throw away chunks that have been gap ACKed.  */
+	list_for_each_safe(lchunk, temp, &q->sacked) {
+		list_del(lchunk);
+		chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
+		sctp_free_chunk(chunk);
+	}
+
+	/* Throw away any chunks in the retransmit queue. */
+	list_for_each_safe(lchunk, temp, &q->retransmit) {
+		list_del(lchunk);
+		chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
+		sctp_free_chunk(chunk);
+	}
+
+	/* Throw away any leftover data chunks. */
+	while ((chunk = sctp_outq_dequeue_data(q))) {
+
+		/* Generate a SEND FAILED event. */
+		ev = sctp_ulpevent_make_send_failed(q->asoc,
+				chunk, SCTP_DATA_UNSENT,
+				q->error, GFP_ATOMIC);
+		if (ev)
+			sctp_ulpq_tail_event(&q->asoc->ulpq, ev);
+
+		sctp_free_chunk(chunk);
+	}
+
+	q->error = 0;
+
+	/* Throw away any leftover control chunks. */
+	while ((chunk = (sctp_chunk_t *) skb_dequeue(&q->control)))
+		sctp_free_chunk(chunk);
+}
+
+/* Free the outqueue structure and any related pending chunks.  */
+void sctp_outq_free(struct sctp_outq *q)
+{
+	/* Throw away leftover chunks. */
+	sctp_outq_teardown(q);
+
+	/* If we were kmalloc()'d, free the memory.  */
+	if (q->malloced)
+		kfree(q);
+}
+
+/* Put a new chunk in an sctp_outq.  */
+int sctp_outq_tail(struct sctp_outq *q, sctp_chunk_t *chunk)
+{
+	int error = 0;
+
+	SCTP_DEBUG_PRINTK("sctp_outq_tail(%p, %p[%s])\n",
+			  q, chunk, chunk && chunk->chunk_hdr ?
+			  sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type))
+			  : "Illegal Chunk");
+
+	/* If it is data, queue it up, otherwise, send it
+	 * immediately.
+	 */
+	if (SCTP_CID_DATA == chunk->chunk_hdr->type) {
+		/* Is it OK to queue data chunks?  */
+		/* From 9. Termination of Association
+		 *
+		 * When either endpoint performs a shutdown, the
+		 * association on each peer will stop accepting new
+		 * data from its user and only deliver data in queue
+		 * at the time of sending or receiving the SHUTDOWN
+		 * chunk.
+		 */
+		switch (q->asoc->state) {
+		case SCTP_STATE_EMPTY:
+		case SCTP_STATE_CLOSED:
+		case SCTP_STATE_SHUTDOWN_PENDING:
+		case SCTP_STATE_SHUTDOWN_SENT:
+		case SCTP_STATE_SHUTDOWN_RECEIVED:
+		case SCTP_STATE_SHUTDOWN_ACK_SENT:
+			/* Cannot send after transport endpoint shutdown */
+			error = -ESHUTDOWN;
+			break;
+
+		default:
+			SCTP_DEBUG_PRINTK("outqueueing (%p, %p[%s])\n",
+			  q, chunk, chunk && chunk->chunk_hdr ?
+			  sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type))
+			  : "Illegal Chunk");
+
+			sctp_outq_tail_data(q, chunk);
+			if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
+				SCTP_INC_STATS(SctpOutUnorderChunks);
+			else
+				SCTP_INC_STATS(SctpOutOrderChunks);
+			q->empty = 0;
+			break;
+		};
+	} else {
+		__skb_queue_tail(&q->control, (struct sk_buff *) chunk);
+		SCTP_INC_STATS(SctpOutCtrlChunks);
+	}
+
+	if (error < 0)
+		return error;
+
+	error = sctp_outq_flush(q, 0);
+
+	return error;
+}
+
+/* Insert a chunk into the retransmit queue.  Chunks on the retransmit
+ * queue are kept in order, based on the TSNs.
+ */
+void sctp_retransmit_insert(struct list_head *tlchunk, struct sctp_outq *q)
+{
+	struct list_head *rlchunk;
+	sctp_chunk_t *tchunk, *rchunk;
+	__u32 ttsn, rtsn;
+	int done = 0;
+
+	tchunk = list_entry(tlchunk, sctp_chunk_t, transmitted_list);
+	ttsn = ntohl(tchunk->subh.data_hdr->tsn);
+
+	list_for_each(rlchunk, &q->retransmit) {
+		rchunk = list_entry(rlchunk, sctp_chunk_t, transmitted_list);
+		rtsn = ntohl(rchunk->subh.data_hdr->tsn);
+		if (TSN_lt(ttsn, rtsn)) {
+			list_add(tlchunk, rlchunk->prev);
+			done = 1;
+			break;
+		}
+	}
+	if (!done) {
+		list_add_tail(tlchunk, &q->retransmit);
+	}
+}
+
+/* Mark all the eligible packets on a transport for retransmission.  */
+void sctp_retransmit_mark(struct sctp_outq *q,
+			  struct sctp_transport *transport,
+			  __u8 fast_retransmit)
+{
+	struct list_head *lchunk, *ltemp;
+	sctp_chunk_t *chunk;
+
+	/* Walk through the specified transmitted queue.  */
+	list_for_each_safe(lchunk, ltemp, &transport->transmitted) {
+		chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
+
+		/* If we are doing retransmission due to a fast retransmit,
+		 * only the chunk's that are marked for fast retransmit
+		 * should be added to the retransmit queue.  If we are doing
+		 * retransmission due to a timeout or pmtu discovery, only the
+		 * chunks that are not yet acked should be added to the
+		 * retransmit queue.
+		 */
+		if ((fast_retransmit && chunk->fast_retransmit) ||
+		   (!fast_retransmit && !chunk->tsn_gap_acked)) {
+			/* RFC 2960 6.2.1 Processing a Received SACK
+			 *
+			 * C) Any time a DATA chunk is marked for
+			 * retransmission (via either T3-rtx timer expiration
+			 * (Section 6.3.3) or via fast retransmit
+			 * (Section 7.2.4)), add the data size of those
+			 * chunks to the rwnd.
+			 */
+			q->asoc->peer.rwnd += sctp_data_size(chunk);
+			q->outstanding_bytes -= sctp_data_size(chunk);
+			transport->flight_size -= sctp_data_size(chunk);
+
+			/* sctpimpguide-05 Section 2.8.2
+			 * M5) If a T3-rtx timer expires, the
+			 * 'TSN.Missing.Report' of all affected TSNs is set
+			 * to 0.
+			 */
+			chunk->tsn_missing_report = 0;
+
+			/* If a chunk that is being used for RTT measurement
+			 * has to be retransmitted, we cannot use this chunk
+			 * anymore for RTT measurements. Reset rto_pending so
+			 * that a new RTT measurement is started when a new
+			 * data chunk is sent.
+			 */
+			if (chunk->rtt_in_progress) {
+				chunk->rtt_in_progress = 0;
+				transport->rto_pending = 0;
+			}
+
+			/* Move the chunk to the retransmit queue. The chunks
+			 * on the retransmit queue is always kept in order.
+			 */
+			list_del(lchunk);
+			sctp_retransmit_insert(lchunk, q);
+		}
+	}
+
+	SCTP_DEBUG_PRINTK("%s: transport: %p, fast_retransmit: %d, "
+			  "cwnd: %d, ssthresh: %d, flight_size: %d, "
+			  "pba: %d\n", __FUNCTION__,
+			  transport, fast_retransmit,
+			  transport->cwnd, transport->ssthresh,
+			  transport->flight_size,
+			  transport->partial_bytes_acked);
+
+}
+
+/* Mark all the eligible packets on a transport for retransmission and force
+ * one packet out.
+ */
+void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport,
+		     sctp_retransmit_reason_t reason)
+{
+	int error = 0;
+	__u8 fast_retransmit = 0;
+
+	switch(reason) {
+	case SCTP_RTXR_T3_RTX:
+		sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_T3_RTX);
+		/* Update the retran path if the T3-rtx timer has expired for
+		 * the current retran path.
+		 */
+		if (transport == transport->asoc->peer.retran_path)
+			sctp_assoc_update_retran_path(transport->asoc);
+		break;
+	case SCTP_RTXR_FAST_RTX:
+		sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_FAST_RTX);
+		fast_retransmit = 1;
+		break;
+	case SCTP_RTXR_PMTUD:
+	default:
+		break;
+	}
+
+	sctp_retransmit_mark(q, transport, fast_retransmit);
+
+	error = sctp_outq_flush(q, /* rtx_timeout */ 1);
+
+	if (error)
+		q->asoc->base.sk->err = -error;
+}
+
+/*
+ * Transmit DATA chunks on the retransmit queue.  Upon return from
+ * sctp_outq_flush_rtx() the packet 'pkt' may contain chunks which
+ * need to be transmitted by the caller.
+ * We assume that pkt->transport has already been set.
+ *
+ * The return value is a normal kernel error return value.
+ */
+static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
+			       int rtx_timeout, int *start_timer)
+{
+	struct list_head *lqueue;
+	struct list_head *lchunk;
+	struct sctp_transport *transport = pkt->transport;
+	sctp_xmit_t status;
+	sctp_chunk_t *chunk;
+	struct sctp_association *asoc;
+	int error = 0;
+
+	asoc = q->asoc;
+	lqueue = &q->retransmit;
+
+	/* RFC 2960 6.3.3 Handle T3-rtx Expiration
+	 *
+	 * E3) Determine how many of the earliest (i.e., lowest TSN)
+	 * outstanding DATA chunks for the address for which the
+	 * T3-rtx has expired will fit into a single packet, subject
+	 * to the MTU constraint for the path corresponding to the
+	 * destination transport address to which the retransmission
+	 * is being sent (this may be different from the address for
+	 * which the timer expires [see Section 6.4]). Call this value
+	 * K. Bundle and retransmit those K DATA chunks in a single
+	 * packet to the destination endpoint.
+	 *
+	 * [Just to be painfully clear, if we are retransmitting
+	 * because a timeout just happened, we should send only ONE
+	 * packet of retransmitted data.]
+	 */
+	lchunk = sctp_list_dequeue(lqueue);
+
+	while (lchunk) {
+		chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
+
+		/* Make sure that Gap Acked TSNs are not retransmitted.  A
+		 * simple approach is just to move such TSNs out of the
+		 * way and into a 'transmitted' queue and skip to the
+		 * next chunk.
+		 */
+		if (chunk->tsn_gap_acked) {
+			list_add_tail(lchunk, &transport->transmitted);
+			lchunk = sctp_list_dequeue(lqueue);
+			continue;
+		}
+
+		/* Attempt to append this chunk to the packet. */
+		status = (*q->append_output)(pkt, chunk);
+
+		switch (status) {
+		case SCTP_XMIT_PMTU_FULL:
+			/* Send this packet.  */
+			if ((error = (*q->force_output)(pkt)) == 0)
+				*start_timer = 1;
+
+			/* If we are retransmitting, we should only
+			 * send a single packet.
+			 */
+			if (rtx_timeout) {
+				list_add(lchunk, lqueue);
+				lchunk = NULL;
+			}
+
+			/* Bundle lchunk in the next round.  */
+			break;
+
+		case SCTP_XMIT_RWND_FULL:
+		        /* Send this packet. */
+			if ((error = (*q->force_output)(pkt)) == 0)
+				*start_timer = 1;
+
+			/* Stop sending DATA as there is no more room
+			 * at the receiver.
+			 */
+			list_add(lchunk, lqueue);
+			lchunk = NULL;
+			break;
+
+		default:
+			/* The append was successful, so add this chunk to
+			 * the transmitted list.
+			 */
+			list_add_tail(lchunk, &transport->transmitted);
+			*start_timer = 1;
+			q->empty = 0;
+
+			/* Retrieve a new chunk to bundle. */
+			lchunk = sctp_list_dequeue(lqueue);
+			break;
+		};
+	}
+
+	return error;
+}
+
+/* This routine either transmits the fragment or puts it on the output
+ * queue.  'pos' points to the next chunk in the output queue after the
+ * chunk that is currently in the process of fragmentation.
+ */
+void sctp_xmit_frag(struct sctp_outq *q, struct sctp_chunk *pos,
+		struct sctp_packet *packet, struct sctp_chunk *frag, __u32 tsn)
+{
+	struct sctp_transport *transport = packet->transport;
+	struct sk_buff_head *queue = &q->out;
+	sctp_xmit_t status;
+	int error;
+
+	frag->subh.data_hdr->tsn = htonl(tsn);
+	frag->has_tsn = 1;
+
+	/* An inner fragment may be smaller than the earlier one and may get
+	 * in if we call q->build_output.  This ensures that all the fragments
+	 * are sent in order.
+	 */
+	if (!skb_queue_empty(queue)) {
+		SCTP_DEBUG_PRINTK("sctp_xmit_frag: q not empty. "
+				  "adding 0x%x to outqueue\n",
+				  ntohl(frag->subh.data_hdr->tsn));
+		if (pos)
+			sctp_outq_insert_data(q, frag, pos);
+		else
+			sctp_outq_tail_data(q, frag);
+		return;
+	}
+
+	/* Add the chunk fragment to the packet.  */
+	status = (*q->build_output)(packet, frag);
+	switch (status) {
+	case SCTP_XMIT_RWND_FULL:
+		/* RWND is full, so put the chunk in the output queue. */
+		SCTP_DEBUG_PRINTK("sctp_xmit_frag: rwnd full. "
+				  "adding 0x%x to outqueue\n",
+				  ntohl(frag->subh.data_hdr->tsn));
+		if (pos)
+			sctp_outq_insert_data(q, frag, pos);
+		else
+			sctp_outq_tail_data(q, frag);
+		break;
+
+	case SCTP_XMIT_OK:
+		error = (*q->force_output)(packet);
+		if (error < 0) {
+			/* Packet could not be transmitted, put the chunk in
+			 * the output queue
+			 */
+			SCTP_DEBUG_PRINTK("sctp_xmit_frag: force output "
+					  "failed. adding 0x%x to outqueue\n",
+					  ntohl(frag->subh.data_hdr->tsn));
+			if (pos)
+				sctp_outq_insert_data(q, frag, pos);
+			else
+				sctp_outq_tail_data(q, frag);
+		} else {
+			SCTP_DEBUG_PRINTK("sctp_xmit_frag: force output "
+					  "success. 0x%x sent\n",
+					  ntohl(frag->subh.data_hdr->tsn));
+			list_add_tail(&frag->transmitted_list,
+				      &transport->transmitted);
+
+			sctp_transport_reset_timers(transport);
+		}
+		break;
+
+	default:
+		BUG();
+	};
+}
+
+/* This routine calls sctp_xmit_frag() for all the fragments of a message.
+ * The argument 'frag' point to the first fragment and it holds the list
+ * of all the other fragments in the 'frag_list' field.
+ */
+void sctp_xmit_fragmented_chunks(struct sctp_outq *q, struct sctp_packet *pkt,
+				 sctp_chunk_t *frag)
+{
+	struct sctp_association *asoc = frag->asoc;
+	struct list_head *lfrag, *frag_list;
+	__u32 tsn;
+	int nfrags = 1;
+	struct sctp_chunk *pos;
+
+	/* Count the number of fragments. */
+	frag_list = &frag->frag_list;
+	list_for_each(lfrag, frag_list) {
+		nfrags++;
+	}
+
+	/* Get a TSN block of nfrags TSNs. */
+	tsn = sctp_association_get_tsn_block(asoc, nfrags);
+
+	pos = (struct sctp_chunk *)skb_peek(&q->out);
+	/* Transmit the first fragment. */
+	sctp_xmit_frag(q, pos, pkt, frag, tsn++);
+
+	/* Transmit the rest of fragments. */
+	frag_list = &frag->frag_list;
+	list_for_each(lfrag, frag_list) {
+		frag = list_entry(lfrag, sctp_chunk_t, frag_list);
+		sctp_xmit_frag(q, pos, pkt, frag, tsn++);
+	}
+}
+
+/* This routine breaks the given chunk into 'max_frag_data_len' size
+ * fragments.  It returns the first fragment with the frag_list field holding
+ * the remaining fragments.
+ */
+sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk,
+				  size_t max_frag_data_len)
+{
+	struct sctp_association *asoc = chunk->asoc;
+	void *data_ptr = chunk->subh.data_hdr;
+	struct sctp_sndrcvinfo *sinfo = &chunk->sinfo;
+	__u16 chunk_data_len = sctp_data_size(chunk);
+	__u16 ssn = ntohs(chunk->subh.data_hdr->ssn);
+	sctp_chunk_t *first_frag, *frag;
+	struct list_head *frag_list;
+	int nfrags;
+	__u8 old_flags, flags;
+
+	/* nfrags = no. of max size fragments + any smaller last fragment. */
+	nfrags = ((chunk_data_len / max_frag_data_len) +
+		  ((chunk_data_len % max_frag_data_len) ? 1 : 0));
+
+	/* Start of the data in the chunk. */
+	data_ptr += sizeof(sctp_datahdr_t);
+
+	/* Are we fragmenting an already fragmented large message? */
+ 	old_flags = chunk->chunk_hdr->flags;
+	if (old_flags & SCTP_DATA_FIRST_FRAG)
+		flags = SCTP_DATA_FIRST_FRAG;
+	else
+		flags = SCTP_DATA_MIDDLE_FRAG;
+
+	/* Make the first fragment. */
+	first_frag = sctp_make_datafrag(asoc, sinfo, max_frag_data_len,
+					data_ptr, flags, ssn);
+
+	if (!first_frag)
+		goto err;
+	first_frag->has_ssn = 1;
+	/* All the fragments are added to the frag_list of the first chunk. */
+	frag_list = &first_frag->frag_list;
+
+	chunk_data_len -= max_frag_data_len;
+	data_ptr += max_frag_data_len;
+
+	/* Make the middle fragments. */
+	while (chunk_data_len > max_frag_data_len) {
+		frag = sctp_make_datafrag(asoc, sinfo, max_frag_data_len,
+					  data_ptr, SCTP_DATA_MIDDLE_FRAG,
+					  ssn);
+		if (!frag)
+			goto err;
+		frag->has_ssn = 1;
+		/* Add the middle fragment to the first fragment's
+		 * frag_list.
+		 */
+		list_add_tail(&frag->frag_list, frag_list);
+
+		chunk_data_len -= max_frag_data_len;
+		data_ptr += max_frag_data_len;
+	}
+
+	if (old_flags & SCTP_DATA_LAST_FRAG)
+		flags = SCTP_DATA_LAST_FRAG;
+	else
+		flags = SCTP_DATA_MIDDLE_FRAG;
+
+	/* Make the last fragment. */
+	frag = sctp_make_datafrag(asoc, sinfo, chunk_data_len, data_ptr,
+				  flags, ssn);
+	if (!frag)
+		goto err;
+	frag->has_ssn = 1;
+
+	/* Add the last fragment to the first fragment's frag_list. */
+	list_add_tail(&frag->frag_list, frag_list);
+
+	/* Free the original chunk. */
+	sctp_free_chunk(chunk);
+
+	return first_frag;
+
+err:
+	/* Free any fragments that are created before the failure.  */
+	if (first_frag) {
+		struct list_head *flist, *lfrag;
+
+		/* Free all the fragments off the first one. */
+		flist = &first_frag->frag_list;
+		while (NULL != (lfrag = sctp_list_dequeue(flist))) {
+			frag = list_entry(lfrag, sctp_chunk_t, frag_list);
+			sctp_free_chunk(frag);
+		}
+
+		/* Free the first fragment. */
+		sctp_free_chunk(first_frag);
+	}
+
+	return NULL;
+}
+
+/*
+ * sctp_outq_flush - Try to flush an outqueue.
+ *
+ * Description: Send everything in q which we legally can, subject to
+ * congestion limitations.
+ * * Note: This function can be called from multiple contexts so appropriate
+ * locking concerns must be made.  Today we use the sock lock to protect
+ * this function.
+ */
+int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
+{
+	struct sctp_packet *packet;
+	struct sctp_packet singleton;
+	struct sctp_association *asoc = q->asoc;
+	int ecn_capable = asoc->peer.ecn_capable;
+	__u16 sport = asoc->base.bind_addr.port;
+	__u16 dport = asoc->peer.port;
+	__u32 vtag = asoc->peer.i.init_tag;
+	/* This is the ECNE handler for singleton packets.  */
+	sctp_packet_phandler_t *s_ecne_handler = NULL;
+	sctp_packet_phandler_t *ecne_handler = NULL;
+	struct sk_buff_head *queue;
+	struct sctp_transport *transport = NULL;
+	struct sctp_transport *new_transport;
+	sctp_chunk_t *chunk;
+	sctp_xmit_t status;
+	int error = 0;
+	int start_timer = 0;
+
+	/* These transports have chunks to send. */
+	struct list_head transport_list;
+	struct list_head *ltransport;
+
+	INIT_LIST_HEAD(&transport_list);
+	packet = NULL;
+
+	/*
+	 * 6.10 Bundling
+	 *   ...
+	 *   When bundling control chunks with DATA chunks, an
+	 *   endpoint MUST place control chunks first in the outbound
+	 *   SCTP packet.  The transmitter MUST transmit DATA chunks
+	 *   within a SCTP packet in increasing order of TSN.
+	 *   ...
+	 */
+	if (ecn_capable) {
+		s_ecne_handler = &sctp_get_no_prepend;
+		ecne_handler = &sctp_get_ecne_prepend;
+	}
+
+	queue = &q->control;
+	while ((chunk = (sctp_chunk_t *)skb_dequeue(queue))) {
+		/* Pick the right transport to use. */
+		new_transport = chunk->transport;
+
+		if (!new_transport) {
+			new_transport = asoc->peer.active_path;
+		} else if (!new_transport->active) {
+			/* If the chunk is Heartbeat, send it to
+			 * chunk->transport, even it's inactive.
+			 */
+			if (chunk->chunk_hdr->type != SCTP_CID_HEARTBEAT)
+				new_transport = asoc->peer.active_path;
+		}
+
+		/* Are we switching transports?
+		 * Take care of transport locks.
+		 */
+		if (new_transport != transport) {
+			transport = new_transport;
+			if (list_empty(&transport->send_ready)) {
+				list_add_tail(&transport->send_ready,
+					      &transport_list);
+			}
+			packet = &transport->packet;
+			(*q->config_output)(packet, vtag,
+					    ecn_capable, ecne_handler);
+		}
+
+		switch (chunk->chunk_hdr->type) {
+		/*
+		 * 6.10 Bundling
+		 *   ...
+		 *   An endpoint MUST NOT bundle INIT, INIT ACK or SHUTDOWN
+		 *   COMPLETE with any other chunks.  [Send them immediately.]
+		 */
+		case SCTP_CID_INIT:
+		case SCTP_CID_INIT_ACK:
+		case SCTP_CID_SHUTDOWN_COMPLETE:
+			(*q->init_output)(&singleton, transport, sport, dport);
+			(*q->config_output)(&singleton, vtag, ecn_capable,
+					    s_ecne_handler);
+			(void) (*q->build_output)(&singleton, chunk);
+			error = (*q->force_output)(&singleton);
+			if (error < 0)
+				return error;
+			break;
+
+		case SCTP_CID_ABORT:
+		case SCTP_CID_SACK:
+		case SCTP_CID_HEARTBEAT:
+		case SCTP_CID_HEARTBEAT_ACK:
+		case SCTP_CID_SHUTDOWN:
+		case SCTP_CID_SHUTDOWN_ACK:
+		case SCTP_CID_ERROR:
+		case SCTP_CID_COOKIE_ECHO:
+		case SCTP_CID_COOKIE_ACK:
+		case SCTP_CID_ECN_ECNE:
+		case SCTP_CID_ECN_CWR:
+			(void) (*q->build_output)(packet, chunk);
+			break;
+
+		case SCTP_CID_ASCONF:
+		case SCTP_CID_ASCONF_ACK:
+			(void) (*q->build_output)(packet, chunk);
+			break;
+
+		default:
+			/* We built a chunk with an illegal type! */
+			BUG();
+		};
+	}
+
+	/* Is it OK to send data chunks?  */
+	switch (asoc->state) {
+	case SCTP_STATE_COOKIE_ECHOED:
+		/* Only allow bundling when this packet has a COOKIE-ECHO
+		 * chunk.
+		 */
+		if (!packet || !packet->has_cookie_echo)
+			break;
+
+		/* fallthru */
+	case SCTP_STATE_ESTABLISHED:
+	case SCTP_STATE_SHUTDOWN_PENDING:
+	case SCTP_STATE_SHUTDOWN_RECEIVED:
+		/*
+		 * RFC 2960 6.1  Transmission of DATA Chunks
+		 *
+		 * C) When the time comes for the sender to transmit,
+		 * before sending new DATA chunks, the sender MUST
+		 * first transmit any outstanding DATA chunks which
+		 * are marked for retransmission (limited by the
+		 * current cwnd).
+		 */
+		if (!list_empty(&q->retransmit)) {
+			if (transport == asoc->peer.retran_path)
+				goto retran;
+
+			/* Switch transports & prepare the packet.  */
+
+			transport = asoc->peer.retran_path;
+
+			if (list_empty(&transport->send_ready)) {
+				list_add_tail(&transport->send_ready,
+					      &transport_list);
+			}
+
+			packet = &transport->packet;
+			(*q->config_output)(packet, vtag,
+					    ecn_capable, ecne_handler);
+		retran:
+			error = sctp_outq_flush_rtx(q, packet,
+						    rtx_timeout, &start_timer);
+
+			if (start_timer)
+				sctp_transport_reset_timers(transport);
+
+			/* This can happen on COOKIE-ECHO resend.  Only
+			 * one chunk can get bundled with a COOKIE-ECHO.
+			 */
+			if (packet->has_cookie_echo)
+				goto sctp_flush_out;
+
+			/* Don't send new data if there is still data
+			 * waiting to retransmit.
+			 */
+			if (!list_empty(&q->retransmit))
+				goto sctp_flush_out;
+		}
+
+		/* Finally, transmit new packets.  */
+		start_timer = 0;
+		queue = &q->out;
+
+		while ((chunk = sctp_outq_dequeue_data(q))) {
+			/* RFC 2960 6.5 Every DATA chunk MUST carry a valid
+			 * stream identifier.
+			 */
+			if (chunk->sinfo.sinfo_stream >=
+			    asoc->c.sinit_num_ostreams) {
+				struct sctp_ulpevent *ev;
+
+				/* Generate a SEND FAILED event. */
+				ev = sctp_ulpevent_make_send_failed(asoc,
+					    chunk, SCTP_DATA_UNSENT,
+					    SCTP_ERROR_INV_STRM, GFP_ATOMIC);
+				if (ev)
+					sctp_ulpq_tail_event(&asoc->ulpq, ev);
+
+				/* Free the chunk. */
+				sctp_free_chunk(chunk);
+				continue;
+			}
+
+			/* Now do delayed assignment of SSN.  This will
+			 * probably change again when we start supporting
+			 * large (> approximately 2^16) size messages.
+			 */
+			sctp_chunk_assign_ssn(chunk);
+
+			/* If there is a specified transport, use it.
+			 * Otherwise, we want to use the active path.
+			 */
+			new_transport = chunk->transport;
+			if (new_transport == NULL ||
+			    !new_transport->active)
+				new_transport = asoc->peer.active_path;
+
+			/* Change packets if necessary.  */
+			if (new_transport != transport) {
+				transport = new_transport;
+
+				/* Schedule to have this transport's
+				 * packet flushed.
+				 */
+				if (list_empty(&transport->send_ready)) {
+					list_add_tail(&transport->send_ready,
+						      &transport_list);
+				}
+
+				packet = &transport->packet;
+				(*q->config_output)(packet, vtag,
+						    ecn_capable, ecne_handler);
+			}
+
+			SCTP_DEBUG_PRINTK("sctp_transmit_packet(%p, %p[%s]), ",
+					  q, chunk,
+					  chunk && chunk->chunk_hdr ?
+					  sctp_cname(SCTP_ST_CHUNK(
+						  chunk->chunk_hdr->type))
+					  : "Illegal Chunk");
+
+			SCTP_DEBUG_PRINTK("TX TSN 0x%x skb->head "
+					"%p skb->users %d.\n",
+					ntohl(chunk->subh.data_hdr->tsn),
+					chunk->skb ?chunk->skb->head : 0,
+					chunk->skb ?
+					atomic_read(&chunk->skb->users) : -1);
+
+			/* Add the chunk to the packet.  */
+			status = (*q->build_output)(packet, chunk);
+
+			switch (status) {
+			case SCTP_XMIT_PMTU_FULL:
+			case SCTP_XMIT_RWND_FULL:
+			case SCTP_XMIT_NAGLE_DELAY:
+				/* We could not append this chunk, so put
+				 * the chunk back on the output queue.
+				 */
+				SCTP_DEBUG_PRINTK("sctp_outq_flush: could "
+					"not transmit TSN: 0x%x, status: %d\n",
+					ntohl(chunk->subh.data_hdr->tsn),
+					status);
+				sctp_outq_head_data(q, chunk);
+				goto sctp_flush_out;
+				break;
+
+			case SCTP_XMIT_MUST_FRAG: {
+				sctp_chunk_t *frag;
+
+				frag = sctp_fragment_chunk(chunk,
+					packet->transport->asoc->frag_point);
+				if (!frag) {
+					/* We could not fragment due to out of
+					 * memory condition. Free the original
+					 * chunk and return ENOMEM.
+					 */
+					sctp_free_chunk(chunk);
+					error = -ENOMEM;
+					return error;
+				}
+
+				sctp_xmit_fragmented_chunks(q, packet, frag);
+				goto sctp_flush_out;
+				break;
+			}
+
+			case SCTP_XMIT_OK:
+				break;
+
+			default:
+				BUG();
+			}
+
+			/* BUG: We assume that the (*q->force_output())
+			 * call below will succeed all the time and add the
+			 * chunk to the transmitted list and restart the
+			 * timers.
+			 * It is possible that the call can fail under OOM
+			 * conditions.
+			 *
+			 * Is this really a problem?  Won't this behave
+			 * like a lost TSN?
+			 */
+			list_add_tail(&chunk->transmitted_list,
+				      &transport->transmitted);
+
+			sctp_transport_reset_timers(transport);
+
+			q->empty = 0;
+
+			/* Only let one DATA chunk get bundled with a
+			 * COOKIE-ECHO chunk.
+			 */
+			if (packet->has_cookie_echo)
+				goto sctp_flush_out;
+		}
+		break;
+
+	default:
+		/* Do nothing.  */
+		break;
+	}
+
+sctp_flush_out:
+
+	/* Before returning, examine all the transports touched in
+	 * this call.  Right now, we bluntly force clear all the
+	 * transports.  Things might change after we implement Nagle.
+	 * But such an examination is still required.
+	 *
+	 * --xguo
+	 */
+	while ((ltransport = sctp_list_dequeue(&transport_list)) != NULL ) {
+		struct sctp_transport *t = list_entry(ltransport,
+						      struct sctp_transport,
+						      send_ready);
+		if (t != transport)
+			transport = t;
+
+		packet = &transport->packet;
+		if (packet->size != SCTP_IP_OVERHEAD)
+			error = (*q->force_output)(packet);
+	}
+
+	return error;
+}
+
+/* Set the various output handling callbacks.  */
+int sctp_outq_set_output_handlers(struct sctp_outq *q,
+				      sctp_outq_ohandler_init_t init,
+				      sctp_outq_ohandler_config_t config,
+				      sctp_outq_ohandler_t append,
+				      sctp_outq_ohandler_t build,
+				      sctp_outq_ohandler_force_t force)
+{
+	q->init_output = init;
+	q->config_output = config;
+	q->append_output = append;
+	q->build_output = build;
+	q->force_output = force;
+	return 0;
+}
+
+/* Update unack_data based on the incoming SACK chunk */
+static void sctp_sack_update_unack_data(struct sctp_association *assoc,
+					sctp_sackhdr_t *sack)
+{
+	sctp_sack_variable_t *frags;
+	__u16 unack_data;
+	int i;
+
+	unack_data = assoc->next_tsn - assoc->ctsn_ack_point - 1;
+
+	frags = sack->variable;
+	for (i = 0; i < ntohs(sack->num_gap_ack_blocks); i++) {
+		unack_data -= ((ntohs(frags[i].gab.end) -
+				ntohs(frags[i].gab.start) + 1));
+	}
+
+	assoc->unack_data = unack_data;
+}
+
+/* Return the highest new tsn that is acknowledged by the given SACK chunk. */
+static __u32 sctp_highest_new_tsn(sctp_sackhdr_t *sack,
+				  struct sctp_association *asoc)
+{
+	struct list_head *ltransport, *lchunk;
+	struct sctp_transport *transport;
+	sctp_chunk_t *chunk;
+	__u32 highest_new_tsn, tsn;
+	struct list_head *transport_list = &asoc->peer.transport_addr_list;
+
+	highest_new_tsn = ntohl(sack->cum_tsn_ack);
+
+	list_for_each(ltransport, transport_list) {
+		transport = list_entry(ltransport, struct sctp_transport,
+				       transports);
+		list_for_each(lchunk, &transport->transmitted) {
+			chunk = list_entry(lchunk, sctp_chunk_t,
+					   transmitted_list);
+			tsn = ntohl(chunk->subh.data_hdr->tsn);
+
+			if (!chunk->tsn_gap_acked &&
+			    TSN_lt(highest_new_tsn, tsn) &&
+			    sctp_acked(sack, tsn))
+				highest_new_tsn = tsn;
+		}
+	}
+
+	return highest_new_tsn;
+}
+
+/* This is where we REALLY process a SACK.
+ *
+ * Process the SACK against the outqueue.  Mostly, this just frees
+ * things off the transmitted queue.
+ */
+int sctp_outq_sack(struct sctp_outq *q, sctp_sackhdr_t *sack)
+{
+	struct sctp_association *asoc = q->asoc;
+	struct sctp_transport *transport;
+	sctp_chunk_t *tchunk;
+	struct list_head *lchunk, *transport_list, *pos;
+	sctp_sack_variable_t *frags = sack->variable;
+	__u32 sack_ctsn, ctsn, tsn;
+	__u32 highest_tsn, highest_new_tsn;
+	__u32 sack_a_rwnd;
+	int outstanding;
+
+	/* Grab the association's destination address list. */
+	transport_list = &asoc->peer.transport_addr_list;
+
+	sack_ctsn = ntohl(sack->cum_tsn_ack);
+
+	/* Get the highest TSN in the sack. */
+	highest_tsn = sack_ctsn +
+		      ntohs(frags[ntohs(sack->num_gap_ack_blocks) - 1].gab.end);
+
+	if (TSN_lt(asoc->highest_sacked, highest_tsn)) {
+		highest_new_tsn = highest_tsn;
+		asoc->highest_sacked = highest_tsn;
+	} else {
+		highest_new_tsn = sctp_highest_new_tsn(sack, asoc);
+	}
+
+	/* Run through the retransmit queue.  Credit bytes received
+	 * and free those chunks that we can.
+	 */
+	sctp_check_transmitted(q, &q->retransmit, NULL, sack, highest_new_tsn);
+
+	/* Run through the transmitted queue.
+	 * Credit bytes received and free those chunks which we can.
+	 *
+	 * This is a MASSIVE candidate for optimization.
+	 */
+	list_for_each(pos, transport_list) {
+		transport  = list_entry(pos, struct sctp_transport,
+					transports);
+		sctp_check_transmitted(q, &transport->transmitted,
+				       transport, sack, highest_new_tsn);
+	}
+
+	/* Move the Cumulative TSN Ack Point if appropriate.  */
+	if (TSN_lt(asoc->ctsn_ack_point, sack_ctsn))
+		asoc->ctsn_ack_point = sack_ctsn;
+
+	/* Update unack_data field in the assoc. */
+	sctp_sack_update_unack_data(asoc, sack);
+
+	ctsn = asoc->ctsn_ack_point;
+
+	SCTP_DEBUG_PRINTK("%s: sack Cumulative TSN Ack is 0x%x.\n",
+			  __FUNCTION__, sack_ctsn);
+	SCTP_DEBUG_PRINTK("%s: Cumulative TSN Ack of association "
+			  "%p is 0x%x.\n", __FUNCTION__, asoc, ctsn);
+
+	/* Throw away stuff rotting on the sack queue.  */
+	list_for_each(lchunk, &q->sacked) {
+		tchunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
+		tsn = ntohl(tchunk->subh.data_hdr->tsn);
+		if (TSN_lte(tsn, ctsn)) {
+			lchunk = lchunk->prev;
+			sctp_free_chunk(tchunk);
+		}
+	}
+
+	/* ii) Set rwnd equal to the newly received a_rwnd minus the
+	 *     number of bytes still outstanding after processing the
+	 *     Cumulative TSN Ack and the Gap Ack Blocks.
+	 */
+
+	sack_a_rwnd = ntohl(sack->a_rwnd);
+	outstanding = q->outstanding_bytes;
+
+	if (outstanding < sack_a_rwnd)
+		sack_a_rwnd -= outstanding;
+	else
+		sack_a_rwnd = 0;
+
+	asoc->peer.rwnd = sack_a_rwnd;
+
+	/* See if all chunks are acked.
+	 * Make sure the empty queue handler will get run later.
+	 */
+	q->empty = skb_queue_empty(&q->out) && list_empty(&q->retransmit);
+	if (!q->empty)
+		goto finish;
+
+	list_for_each(pos, transport_list) {
+		transport  = list_entry(pos, struct sctp_transport,
+					transports);
+		q->empty = q->empty && list_empty(&transport->transmitted);
+		if (!q->empty)
+			goto finish;
+	}
+
+	SCTP_DEBUG_PRINTK("sack queue is empty.\n");
+finish:
+	return q->empty;
+}
+
+/* Is the outqueue empty?  */
+int sctp_outq_is_empty(const struct sctp_outq *q)
+{
+	return q->empty;
+}
+
+/********************************************************************
+ * 2nd Level Abstractions
+ ********************************************************************/
+
+/* Go through a transport's transmitted list or the assocication's retransmit
+ * list and move chunks that are acked by the Cumulative TSN Ack to q->sacked.
+ * The retransmit list will not have an associated transport. In case of a
+ * transmitted list with a transport, the transport's congestion, rto and fast
+ * retransmit parameters are also updated and if needed a fast retransmit
+ * process is started.
+ *
+ * I added coherent debug information output.	--xguo
+ *
+ * Instead of printing 'sacked' or 'kept' for each TSN on the
+ * transmitted_queue, we print a range: SACKED: TSN1-TSN2, TSN3, TSN4-TSN5.
+ * KEPT TSN6-TSN7, etc.
+ */
+static void sctp_check_transmitted(struct sctp_outq *q,
+				   struct list_head *transmitted_queue,
+				   struct sctp_transport *transport,
+				   sctp_sackhdr_t *sack,
+				   __u32 highest_new_tsn_in_sack)
+{
+	struct list_head *lchunk;
+	sctp_chunk_t *tchunk;
+	struct list_head tlist;
+	__u32 tsn;
+	__u32 sack_ctsn;
+	__u32 rtt;
+	__u8 restart_timer = 0;
+	__u8 do_fast_retransmit = 0;
+	int bytes_acked = 0;
+
+	/* These state variables are for coherent debug output. --xguo */
+
+#if SCTP_DEBUG
+	__u32 dbg_ack_tsn = 0;	/* An ACKed TSN range starts here... */
+	__u32 dbg_last_ack_tsn = 0;  /* ...and finishes here.	     */
+	__u32 dbg_kept_tsn = 0;	/* An un-ACKed range starts here...  */
+	__u32 dbg_last_kept_tsn = 0; /* ...and finishes here.	     */
+
+	/* 0 : The last TSN was ACKed.
+	 * 1 : The last TSN was NOT ACKed (i.e. KEPT).
+	 * -1: We need to initialize.
+	 */
+	int dbg_prt_state = -1;
+#endif /* SCTP_DEBUG */
+
+	sack_ctsn = ntohl(sack->cum_tsn_ack);
+
+	INIT_LIST_HEAD(&tlist);
+
+	/* The while loop will skip empty transmitted queues. */
+	while (NULL != (lchunk = sctp_list_dequeue(transmitted_queue))) {
+		tchunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
+
+		tsn = ntohl(tchunk->subh.data_hdr->tsn);
+		if (sctp_acked(sack, tsn)) {
+			/* If this queue is the retransmit queue, the
+			 * retransmit timer has already reclaimed
+			 * the outstanding bytes for this chunk, so only
+			 * count bytes associated with a transport.
+			 */
+			if (transport) {
+				/* If this chunk is being used for RTT
+				 * measurement, calculate the RTT and update
+				 * the RTO using this value.
+				 *
+				 * 6.3.1 C5) Karn's algorithm: RTT measurements
+				 * MUST NOT be made using packets that were
+				 * retransmitted (and thus for which it is
+				 * ambiguous whether the reply was for the
+				 * first instance of the packet or a later
+				 * instance).
+				 */
+			   	if ((!tchunk->tsn_gap_acked) &&
+				    (1 == tchunk->num_times_sent) &&
+				    (tchunk->rtt_in_progress)) {
+					rtt = jiffies - tchunk->sent_at;
+					sctp_transport_update_rto(transport,
+								  rtt);
+				}
+			}
+                        if (TSN_lte(tsn, sack_ctsn)) {
+				/* RFC 2960  6.3.2 Retransmission Timer Rules
+				 *
+				 * R3) Whenever a SACK is received
+				 * that acknowledges the DATA chunk
+				 * with the earliest outstanding TSN
+				 * for that address, restart T3-rtx
+				 * timer for that address with its
+				 * current RTO.
+				 */
+				restart_timer = 1;
+
+				if (!tchunk->tsn_gap_acked) {
+					tchunk->tsn_gap_acked = 1;
+					bytes_acked += sctp_data_size(tchunk);
+				}
+
+				list_add_tail(&tchunk->transmitted_list,
+					      &q->sacked);
+			} else {
+				/* RFC2960 7.2.4, sctpimpguide-05 2.8.2
+				 * M2) Each time a SACK arrives reporting
+				 * 'Stray DATA chunk(s)' record the highest TSN
+				 * reported as newly acknowledged, call this
+				 * value 'HighestTSNinSack'. A newly
+				 * acknowledged DATA chunk is one not
+				 * previously acknowledged in a SACK.
+				 *
+				 * When the SCTP sender of data receives a SACK
+				 * chunk that acknowledges, for the first time,
+				 * the receipt of a DATA chunk, all the still
+				 * unacknowledged DATA chunks whose TSN is
+				 * older than that newly acknowledged DATA
+				 * chunk, are qualified as 'Stray DATA chunks'.
+				 */
+				if (!tchunk->tsn_gap_acked) {
+					tchunk->tsn_gap_acked = 1;
+					bytes_acked += sctp_data_size(tchunk);
+				}
+				list_add_tail(lchunk, &tlist);
+			}
+
+#if SCTP_DEBUG
+			switch (dbg_prt_state) {
+			case 0:	/* last TSN was ACKed */
+				if (dbg_last_ack_tsn + 1 == tsn) {
+					/* This TSN belongs to the
+					 * current ACK range.
+					 */
+					break;
+				}
+
+				if (dbg_last_ack_tsn != dbg_ack_tsn) {
+					/* Display the end of the
+					 * current range.
+					 */
+					SCTP_DEBUG_PRINTK("-%08x",
+							  dbg_last_ack_tsn);
+				}
+
+				/* Start a new range.  */
+				SCTP_DEBUG_PRINTK(",%08x", tsn);
+				dbg_ack_tsn = tsn;
+				break;
+
+			case 1:	/* The last TSN was NOT ACKed. */
+				if (dbg_last_kept_tsn != dbg_kept_tsn) {
+					/* Display the end of current range. */
+					SCTP_DEBUG_PRINTK("-%08x",
+							  dbg_last_kept_tsn);
+				}
+
+				SCTP_DEBUG_PRINTK("\n");
+
+				/* FALL THROUGH... */
+			default:
+				/* This is the first-ever TSN we examined.  */
+				/* Start a new range of ACK-ed TSNs.  */
+				SCTP_DEBUG_PRINTK("ACKed: %08x", tsn);
+				dbg_prt_state = 0;
+				dbg_ack_tsn = tsn;
+			};
+
+			dbg_last_ack_tsn = tsn;
+#endif /* SCTP_DEBUG */
+
+		} else {
+			if (tchunk->tsn_gap_acked) {
+				SCTP_DEBUG_PRINTK("%s: Receiver reneged on "
+						  "data TSN: 0x%x\n",
+						  __FUNCTION__,
+						  tsn);
+				tchunk->tsn_gap_acked = 0;
+
+				bytes_acked -= sctp_data_size(tchunk);
+
+				/* RFC 2960 6.3.2 Retransmission Timer Rules
+				 *
+				 * R4) Whenever a SACK is received missing a
+				 * TSN that was previously acknowledged via a
+				 * Gap Ack Block, start T3-rtx for the
+				 * destination address to which the DATA
+				 * chunk was originally
+				 * transmitted if it is not already running.
+				 */
+				restart_timer = 1;
+			}
+
+			list_add_tail(lchunk, &tlist);
+
+#if SCTP_DEBUG
+			/* See the above comments on ACK-ed TSNs. */
+			switch (dbg_prt_state) {
+			case 1:
+				if (dbg_last_kept_tsn + 1 == tsn)
+					break;
+
+				if (dbg_last_kept_tsn != dbg_kept_tsn)
+					SCTP_DEBUG_PRINTK("-%08x",
+							  dbg_last_kept_tsn);
+
+				SCTP_DEBUG_PRINTK(",%08x", tsn);
+				dbg_kept_tsn = tsn;
+				break;
+
+			case 0:
+				if (dbg_last_ack_tsn != dbg_ack_tsn)
+					SCTP_DEBUG_PRINTK("-%08x",
+							  dbg_last_ack_tsn);
+				SCTP_DEBUG_PRINTK("\n");
+
+				/* FALL THROUGH... */
+			default:
+				SCTP_DEBUG_PRINTK("KEPT: %08x",tsn);
+				dbg_prt_state = 1;
+				dbg_kept_tsn = tsn;
+			};
+
+			dbg_last_kept_tsn = tsn;
+#endif /* SCTP_DEBUG */
+		}
+	}
+
+#if SCTP_DEBUG
+	/* Finish off the last range, displaying its ending TSN.  */
+	switch (dbg_prt_state) {
+	case 0:
+		if (dbg_last_ack_tsn != dbg_ack_tsn) {
+			SCTP_DEBUG_PRINTK("-%08x\n", dbg_last_ack_tsn);
+		} else {
+			SCTP_DEBUG_PRINTK("\n");
+		}
+	break;
+
+	case 1:
+		if (dbg_last_kept_tsn != dbg_kept_tsn) {
+			SCTP_DEBUG_PRINTK("-%08x\n", dbg_last_kept_tsn);
+		} else {
+			SCTP_DEBUG_PRINTK("\n");
+		}
+	};
+#endif /* SCTP_DEBUG */
+	if (transport) {
+		if (bytes_acked) {
+			/* 8.2. When an outstanding TSN is acknowledged,
+			 * the endpoint shall clear the error counter of
+			 * the destination transport address to which the
+			 * DATA chunk was last sent.
+			 * The association's overall error counter is
+			 * also cleared.
+			 */
+			transport->error_count = 0;
+			transport->asoc->overall_error_count = 0;
+
+			/* Mark the destination transport address as
+			 * active if it is not so marked.
+			 */
+			if (!transport->active) {
+				sctp_assoc_control_transport(
+					transport->asoc,
+					transport,
+					SCTP_TRANSPORT_UP,
+					SCTP_RECEIVED_SACK);
+			}
+
+			sctp_transport_raise_cwnd(transport, sack_ctsn,
+						  bytes_acked);
+
+			transport->flight_size -= bytes_acked;
+			q->outstanding_bytes -= bytes_acked;
+		} else {
+			/* RFC 2960 6.1, sctpimpguide-06 2.15.2
+			 * When a sender is doing zero window probing, it
+			 * should not timeout the association if it continues
+			 * to receive new packets from the receiver. The
+			 * reason is that the receiver MAY keep its window
+			 * closed for an indefinite time.
+			 * A sender is doing zero window probing when the
+			 * receiver's advertised window is zero, and there is
+			 * only one data chunk in flight to the receiver.
+			 */
+			if ((0 == q->asoc->peer.rwnd) &&
+			    (!list_empty(&tlist)) &&
+			    (sack_ctsn+2 == q->asoc->next_tsn)) {
+				SCTP_DEBUG_PRINTK("%s: SACK received for zero "
+						  "window probe: %u\n",
+						  __FUNCTION__, sack_ctsn);
+				q->asoc->overall_error_count = 0;
+				transport->error_count = 0;
+			}
+		}
+
+		/* RFC 2960 6.3.2 Retransmission Timer Rules
+		 *
+		 * R2) Whenever all outstanding data sent to an address have
+		 * been acknowledged, turn off the T3-rtx timer of that
+		 * address.
+		 */
+		if (!transport->flight_size) {
+			if (timer_pending(&transport->T3_rtx_timer) &&
+			    del_timer(&transport->T3_rtx_timer)) {
+				sctp_transport_put(transport);
+			}
+		} else if (restart_timer) {
+			if (!mod_timer(&transport->T3_rtx_timer,
+				       jiffies + transport->rto))
+				sctp_transport_hold(transport);
+		}
+	}
+
+	/* Reconstruct the transmitted list with chunks that are not yet
+	 * acked by the Cumulative TSN Ack.
+	 */
+        while (NULL != (lchunk = sctp_list_dequeue(&tlist))) {
+		tchunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
+		tsn = ntohl(tchunk->subh.data_hdr->tsn);
+
+		/* RFC 2960 7.2.4, sctpimpguide-05 2.8.2 M3) Examine all
+		 * 'Unacknowledged TSN's', if the TSN number of an
+		 * 'Unacknowledged TSN' is smaller than the 'HighestTSNinSack'
+		 * value, increment the 'TSN.Missing.Report' count on that
+		 * chunk if it has NOT been fast retransmitted or marked for
+		 * fast retransmit already.
+		 *
+		 * M4) If any DATA chunk is found to have a
+		 * 'TSN.Missing.Report'
+		 * value larger than or equal to 4, mark that chunk for
+		 * retransmission and start the fast retransmit procedure.
+		 */
+		if ((!tchunk->fast_retransmit) &&
+		    (!tchunk->tsn_gap_acked) &&
+		    (TSN_lt(tsn, highest_new_tsn_in_sack))) {
+			tchunk->tsn_missing_report++;
+			SCTP_DEBUG_PRINTK("%s: TSN 0x%x missing counter: %d\n",
+					  __FUNCTION__, tsn,
+					  tchunk->tsn_missing_report);
+		}
+		if (tchunk->tsn_missing_report >= 4) {
+			tchunk->fast_retransmit = 1;
+			do_fast_retransmit = 1;
+		}
+
+		list_add_tail(lchunk, transmitted_queue);
+	}
+
+	if (transport) {
+		if (do_fast_retransmit)
+			sctp_retransmit(q, transport, SCTP_RTXR_FAST_RTX);
+
+		SCTP_DEBUG_PRINTK("%s: transport: %p, cwnd: %d, "
+				  "ssthresh: %d, flight_size: %d, pba: %d\n",
+				  __FUNCTION__, transport, transport->cwnd,
+			  	  transport->ssthresh, transport->flight_size,
+				  transport->partial_bytes_acked);
+	}
+}
+
+/* Is the given TSN acked by this packet?  */
+static int sctp_acked(sctp_sackhdr_t *sack, __u32 tsn)
+{
+	int i;
+	sctp_sack_variable_t *frags;
+	__u16 gap;
+	__u32 ctsn = ntohl(sack->cum_tsn_ack);
+
+        if (TSN_lte(tsn, ctsn))
+		goto pass;
+
+	/* 3.3.4 Selective Acknowledgement (SACK) (3):
+	 *
+	 * Gap Ack Blocks:
+	 *  These fields contain the Gap Ack Blocks. They are repeated
+	 *  for each Gap Ack Block up to the number of Gap Ack Blocks
+	 *  defined in the Number of Gap Ack Blocks field. All DATA
+	 *  chunks with TSNs greater than or equal to (Cumulative TSN
+	 *  Ack + Gap Ack Block Start) and less than or equal to
+	 *  (Cumulative TSN Ack + Gap Ack Block End) of each Gap Ack
+	 *  Block are assumed to have been received correctly.
+	 */
+
+	frags = sack->variable;
+	gap = tsn - ctsn;
+	for (i = 0; i < ntohs(sack->num_gap_ack_blocks); ++i) {
+		if (TSN_lte(ntohs(frags[i].gab.start), gap) &&
+		    TSN_lte(gap, ntohs(frags[i].gab.end)))
+			goto pass;
+	}
+
+	return 0;
+pass:
+	return 1;
+}
diff -urN linux-2.4.23-pre5/net/sctp/primitive.c linux-2.4.23-pre6/net/sctp/primitive.c
--- linux-2.4.23-pre5/net/sctp/primitive.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/primitive.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,205 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * These functions implement the SCTP primitive functions from Section 10.
+ *
+ * Note that the descriptions from the specification are USER level
+ * functions--this file is the functions which populate the struct proto
+ * for SCTP which is the BOTTOM of the sockets interface.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Narasimha Budihal     <narasimha@refcode.org>
+ *    Karl Knutson          <karl@athena.chicago.il.us>
+ *    Ardelle Fan	    <ardelle.fan@intel.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/types.h>
+#include <linux/list.h> /* For struct list_head */
+#include <linux/socket.h>
+#include <linux/ip.h>
+#include <linux/time.h> /* For struct timeval */
+#include <net/sock.h>
+#include <net/sctp/sctp.h>
+#include <net/sctp/sm.h>
+
+#define DECLARE_PRIMITIVE(name) \
+/* This is called in the code as sctp_primitive_ ## name.  */ \
+int sctp_primitive_ ## name(struct sctp_association *asoc, \
+			    void *arg) { \
+	int error = 0; \
+	sctp_event_t event_type; sctp_subtype_t subtype; \
+	sctp_state_t state; \
+	struct sctp_endpoint *ep; \
+	\
+	event_type = SCTP_EVENT_T_PRIMITIVE; \
+	subtype = SCTP_ST_PRIMITIVE(SCTP_PRIMITIVE_ ## name); \
+	state = asoc ? asoc->state : SCTP_STATE_CLOSED; \
+	ep = asoc ? asoc->ep : NULL; \
+	\
+	error = sctp_do_sm(event_type, subtype, state, ep, asoc, \
+			   arg, GFP_KERNEL); \
+ 	return error; \
+}
+
+/* 10.1 ULP-to-SCTP
+ * B) Associate
+ *
+ * Format: ASSOCIATE(local SCTP instance name, destination transport addr,
+ *         outbound stream count)
+ * -> association id [,destination transport addr list] [,outbound stream
+ *    count]
+ *
+ * This primitive allows the upper layer to initiate an association to a
+ * specific peer endpoint.
+ *
+ * This version assumes that asoc is fully populated with the initial
+ * parameters.  We then return a traditional kernel indicator of
+ * success or failure.
+ */
+
+/* This is called in the code as sctp_primitive_ASSOCIATE.  */
+
+DECLARE_PRIMITIVE(ASSOCIATE)
+
+/* 10.1 ULP-to-SCTP
+ * C) Shutdown
+ *
+ * Format: SHUTDOWN(association id)
+ * -> result
+ *
+ * Gracefully closes an association. Any locally queued user data
+ * will be delivered to the peer. The association will be terminated only
+ * after the peer acknowledges all the SCTP packets sent.  A success code
+ * will be returned on successful termination of the association. If
+ * attempting to terminate the association results in a failure, an error
+ * code shall be returned.
+ */
+
+DECLARE_PRIMITIVE(SHUTDOWN);
+
+/* 10.1 ULP-to-SCTP
+ * C) Abort
+ *
+ * Format: Abort(association id [, cause code])
+ * -> result
+ *
+ * Ungracefully closes an association. Any locally queued user data
+ * will be discarded and an ABORT chunk is sent to the peer. A success
+ * code will be returned on successful abortion of the association. If
+ * attempting to abort the association results in a failure, an error
+ * code shall be returned.
+ */
+
+DECLARE_PRIMITIVE(ABORT);
+
+/* 10.1 ULP-to-SCTP
+ * E) Send
+ *
+ * Format: SEND(association id, buffer address, byte count [,context]
+ *         [,stream id] [,life time] [,destination transport address]
+ *         [,unorder flag] [,no-bundle flag] [,payload protocol-id] )
+ * -> result
+ *
+ * This is the main method to send user data via SCTP.
+ *
+ * Mandatory attributes:
+ *
+ *  o association id - local handle to the SCTP association
+ *
+ *  o buffer address - the location where the user message to be
+ *    transmitted is stored;
+ *
+ *  o byte count - The size of the user data in number of bytes;
+ *
+ * Optional attributes:
+ *
+ *  o context - an optional 32 bit integer that will be carried in the
+ *    sending failure notification to the ULP if the transportation of
+ *    this User Message fails.
+ *
+ *  o stream id - to indicate which stream to send the data on. If not
+ *    specified, stream 0 will be used.
+ *
+ *  o life time - specifies the life time of the user data. The user data
+ *    will not be sent by SCTP after the life time expires. This
+ *    parameter can be used to avoid efforts to transmit stale
+ *    user messages. SCTP notifies the ULP if the data cannot be
+ *    initiated to transport (i.e. sent to the destination via SCTP's
+ *    send primitive) within the life time variable. However, the
+ *    user data will be transmitted if SCTP has attempted to transmit a
+ *    chunk before the life time expired.
+ *
+ *  o destination transport address - specified as one of the destination
+ *    transport addresses of the peer endpoint to which this packet
+ *    should be sent. Whenever possible, SCTP should use this destination
+ *    transport address for sending the packets, instead of the current
+ *    primary path.
+ *
+ *  o unorder flag - this flag, if present, indicates that the user
+ *    would like the data delivered in an unordered fashion to the peer
+ *    (i.e., the U flag is set to 1 on all DATA chunks carrying this
+ *    message).
+ *
+ *  o no-bundle flag - instructs SCTP not to bundle this user data with
+ *    other outbound DATA chunks. SCTP MAY still bundle even when
+ *    this flag is present, when faced with network congestion.
+ *
+ *  o payload protocol-id - A 32 bit unsigned integer that is to be
+ *    passed to the peer indicating the type of payload protocol data
+ *    being transmitted. This value is passed as opaque data by SCTP.
+ */
+
+DECLARE_PRIMITIVE(SEND);
+
+/* 10.1 ULP-to-SCTP
+ * J) Request Heartbeat
+ *
+ * Format: REQUESTHEARTBEAT(association id, destination transport address)
+ *
+ * -> result
+ *
+ * Instructs the local endpoint to perform a HeartBeat on the specified
+ * destination transport address of the given association. The returned
+ * result should indicate whether the transmission of the HEARTBEAT
+ * chunk to the destination address is successful.
+ *
+ * Mandatory attributes:
+ *
+ * o association id - local handle to the SCTP association
+ *
+ * o destination transport address - the transport address of the
+ *   association on which a heartbeat should be issued.
+ */
+
+DECLARE_PRIMITIVE(REQUESTHEARTBEAT);
diff -urN linux-2.4.23-pre5/net/sctp/proc.c linux-2.4.23-pre6/net/sctp/proc.c
--- linux-2.4.23-pre5/net/sctp/proc.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/proc.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,129 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 2003 International Business Machines, Corp.
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    Sridhar Samudrala <sri@us.ibm.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/types.h>
+#include <asm/semaphore.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/init.h>
+#include <net/sctp/sctp.h>
+
+static char *sctp_snmp_list[] = {
+#define SCTP_SNMP_ENTRY(x) #x 
+	SCTP_SNMP_ENTRY(SctpCurrEstab),
+	SCTP_SNMP_ENTRY(SctpActiveEstabs),
+	SCTP_SNMP_ENTRY(SctpPassiveEstabs),
+	SCTP_SNMP_ENTRY(SctpAborteds),
+	SCTP_SNMP_ENTRY(SctpShutdowns),
+	SCTP_SNMP_ENTRY(SctpOutOfBlues),
+	SCTP_SNMP_ENTRY(SctpChecksumErrors),
+	SCTP_SNMP_ENTRY(SctpOutCtrlChunks),
+	SCTP_SNMP_ENTRY(SctpOutOrderChunks),
+	SCTP_SNMP_ENTRY(SctpOutUnorderChunks),
+	SCTP_SNMP_ENTRY(SctpInCtrlChunks),
+	SCTP_SNMP_ENTRY(SctpInOrderChunks),
+	SCTP_SNMP_ENTRY(SctpInUnorderChunks),
+	SCTP_SNMP_ENTRY(SctpFragUsrMsgs),
+	SCTP_SNMP_ENTRY(SctpReasmUsrMsgs),
+	SCTP_SNMP_ENTRY(SctpOutSCTPPacks),
+	SCTP_SNMP_ENTRY(SctpInSCTPPacks),
+#undef SCTP_SNMP_ENTRY
+};
+
+/* Return the current value of a particular entry in the mib by adding its
+ * per cpu counters.
+ */ 
+static unsigned long
+fold_field(void *mib[], int nr)
+{
+	unsigned long res = 0;
+	int i;
+	int sz = sizeof(struct sctp_mib);
+	unsigned long* begin;
+
+	sz /= sizeof(unsigned long);
+	begin = (unsigned long*) mib;
+
+	for (i=0; i<smp_num_cpus; i++) {
+		res += begin[2*cpu_logical_map(i)*sz + nr];
+		res += begin[(2*cpu_logical_map(i)+1)*sz + nr];
+	}
+	return res;
+}
+
+/* Display sctp snmp mib statistics(/proc/net/sctp/snmp). */
+static int sctp_snmp_seq_show(struct seq_file *seq, void *v)
+{
+	int i;
+
+	for (i = 0; i < sizeof(sctp_snmp_list) / sizeof(char *); i++)
+		seq_printf(seq, "%-32s\t%ld\n", sctp_snmp_list[i],
+			   fold_field((void **)sctp_statistics, i));
+	return 0;
+}
+
+/* Initialize the seq file operations for 'snmp' object. */
+static int sctp_snmp_seq_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, sctp_snmp_seq_show, NULL);
+}
+
+static struct file_operations sctp_snmp_seq_fops = {
+	.open	 = sctp_snmp_seq_open,
+	.read	 = seq_read,
+	.llseek	 = seq_lseek,
+	.release = single_release,
+};
+
+/* Set up the proc fs entry for 'snmp' object. */
+int __init sctp_snmp_proc_init(void)
+{
+	struct proc_dir_entry *p;
+
+	p = create_proc_entry("snmp", S_IRUGO, proc_net_sctp);
+	if (!p)
+		return -ENOMEM;
+
+	p->proc_fops = &sctp_snmp_seq_fops;
+
+	return 0;
+}
+
+/* Cleanup the proc fs entry for 'snmp' object. */
+void sctp_snmp_proc_exit(void)
+{
+	remove_proc_entry("snmp", proc_net_sctp);
+}
diff -urN linux-2.4.23-pre5/net/sctp/protocol.c linux-2.4.23-pre6/net/sctp/protocol.c
--- linux-2.4.23-pre5/net/sctp/protocol.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/protocol.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,1082 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001-2002 International Business Machines, Corp.
+ * Copyright (c) 2001 Intel Corp.
+ * Copyright (c) 2001 Nokia, Inc.
+ * Copyright (c) 2001 La Monte H.P. Yarroll
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * Initialization/cleanup for SCTP protocol support.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Karl Knutson <karl@athena.chicago.il.us>
+ *    Jon Grimm <jgrimm@us.ibm.com>
+ *    Sridhar Samudrala <sri@us.ibm.com>
+ *    Daisy Chang <daisyc@us.ibm.com>
+ *    Ardelle Fan <ardelle.fan@intel.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <net/protocol.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/sctp/sctp.h>
+#include <net/addrconf.h>
+#include <net/inet_common.h>
+
+/* Global data structures. */
+struct sctp_protocol sctp_proto;
+struct proc_dir_entry	*proc_net_sctp;
+DEFINE_SNMP_STAT(struct sctp_mib, sctp_statistics);
+
+/* This is the global socket data structure used for responding to
+ * the Out-of-the-blue (OOTB) packets.  A control sock will be created
+ * for this socket at the initialization time.
+ */
+static struct socket *sctp_ctl_socket;
+
+static struct sctp_pf *sctp_pf_inet6_specific;
+static struct sctp_pf *sctp_pf_inet_specific;
+static struct sctp_af *sctp_af_v4_specific;
+static struct sctp_af *sctp_af_v6_specific;
+
+extern struct net_proto_family inet_family_ops;
+
+extern int sctp_snmp_proc_init(void);
+extern int sctp_snmp_proc_exit(void);
+
+/* Return the address of the control sock. */
+struct sock *sctp_get_ctl_sock(void)
+{
+	return sctp_ctl_socket->sk;
+}
+
+/* Set up the proc fs entry for the SCTP protocol. */
+__init int sctp_proc_init(void)
+{
+	int rc = 0;
+
+	if (!proc_net_sctp) {
+		struct proc_dir_entry *ent;
+		ent = proc_mkdir("net/sctp", 0);
+		if (ent) {
+			ent->owner = THIS_MODULE;
+			proc_net_sctp = ent;
+		} else
+			rc = -ENOMEM;
+	}
+
+	if (sctp_snmp_proc_init())
+		rc = -ENOMEM;
+
+	return rc;
+}
+
+/* Clean up the proc fs entry for the SCTP protocol. */
+void sctp_proc_exit(void)
+{
+
+	sctp_snmp_proc_exit();
+
+	if (proc_net_sctp) {
+		proc_net_sctp = NULL;
+		remove_proc_entry("net/sctp", 0);
+	}
+}
+
+/* Private helper to extract ipv4 address and stash them in
+ * the protocol structure.
+ */
+static void sctp_v4_copy_addrlist(struct list_head *addrlist,
+				  struct net_device *dev)
+{
+	struct in_device *in_dev;
+	struct in_ifaddr *ifa;
+	struct sockaddr_storage_list *addr;
+
+	read_lock(&inetdev_lock);
+	if ((in_dev = __in_dev_get(dev)) == NULL) {
+		read_unlock(&inetdev_lock);
+		return;
+	}
+
+	read_lock(&in_dev->lock);
+	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
+		/* Add the address to the local list.  */
+		addr = t_new(struct sockaddr_storage_list, GFP_ATOMIC);
+		if (addr) {
+			addr->a.v4.sin_family = AF_INET;
+			addr->a.v4.sin_port = 0;
+			addr->a.v4.sin_addr.s_addr = ifa->ifa_local;
+			list_add_tail(&addr->list, addrlist);
+		}
+	}
+
+	read_unlock(&in_dev->lock);
+	read_unlock(&inetdev_lock);
+}
+
+/* Extract our IP addresses from the system and stash them in the
+ * protocol structure.
+ */
+static void __sctp_get_local_addr_list(struct sctp_protocol *proto)
+{
+	struct net_device *dev;
+	struct list_head *pos;
+	struct sctp_af *af;
+
+	read_lock(&dev_base_lock);
+	for (dev = dev_base; dev; dev = dev->next) {
+		list_for_each(pos, &proto->address_families) {
+			af = list_entry(pos, struct sctp_af, list);
+			af->copy_addrlist(&proto->local_addr_list, dev);
+		}
+	}
+	read_unlock(&dev_base_lock);
+}
+
+static void sctp_get_local_addr_list(struct sctp_protocol *proto)
+{
+	unsigned long flags;
+
+	sctp_spin_lock_irqsave(&sctp_proto.local_addr_lock, flags);
+	__sctp_get_local_addr_list(&sctp_proto);
+	sctp_spin_unlock_irqrestore(&sctp_proto.local_addr_lock, flags);
+}
+
+/* Free the existing local addresses.  */
+static void __sctp_free_local_addr_list(struct sctp_protocol *proto)
+{
+	struct sockaddr_storage_list *addr;
+	struct list_head *pos, *temp;
+
+	list_for_each_safe(pos, temp, &proto->local_addr_list) {
+		addr = list_entry(pos, struct sockaddr_storage_list, list);
+		list_del(pos);
+		kfree(addr);
+	}
+}
+
+/* Free the existing local addresses.  */
+static void sctp_free_local_addr_list(struct sctp_protocol *proto)
+{
+	unsigned long flags;
+
+	sctp_spin_lock_irqsave(&proto->local_addr_lock, flags);
+	__sctp_free_local_addr_list(proto);
+	sctp_spin_unlock_irqrestore(&proto->local_addr_lock, flags);
+}
+
+/* Copy the local addresses which are valid for 'scope' into 'bp'.  */
+int sctp_copy_local_addr_list(struct sctp_protocol *proto,
+			      struct sctp_bind_addr *bp, sctp_scope_t scope,
+			      int gfp, int copy_flags)
+{
+	struct sockaddr_storage_list *addr;
+	int error = 0;
+	struct list_head *pos;
+	unsigned long flags;
+
+	sctp_spin_lock_irqsave(&proto->local_addr_lock, flags);
+	list_for_each(pos, &proto->local_addr_list) {
+		addr = list_entry(pos, struct sockaddr_storage_list, list);
+		if (sctp_in_scope(&addr->a, scope)) {
+			/* Now that the address is in scope, check to see if
+			 * the address type is really supported by the local
+			 * sock as well as the remote peer.
+			 */
+			if ((((AF_INET == addr->a.sa.sa_family) &&
+			      (copy_flags & SCTP_ADDR4_PEERSUPP))) ||
+			    (((AF_INET6 == addr->a.sa.sa_family) &&
+			      (copy_flags & SCTP_ADDR6_ALLOWED) &&
+			      (copy_flags & SCTP_ADDR6_PEERSUPP)))) {
+				error = sctp_add_bind_addr(bp, &addr->a, 
+							   GFP_ATOMIC);
+				if (error)
+					goto end_copy;
+			}
+		}
+	}
+
+end_copy:
+	sctp_spin_unlock_irqrestore(&proto->local_addr_lock, flags);
+	return error;
+}
+
+/* Initialize a sctp_addr from in incoming skb.  */
+static void sctp_v4_from_skb(union sctp_addr *addr, struct sk_buff *skb,
+			     int is_saddr)
+{
+	void *from;
+	__u16 *port;
+	struct sctphdr *sh;
+
+	port = &addr->v4.sin_port;
+	addr->v4.sin_family = AF_INET;
+
+	sh = (struct sctphdr *) skb->h.raw;
+	if (is_saddr) {
+		*port  = ntohs(sh->source);
+		from = &skb->nh.iph->saddr;
+	} else {
+		*port = ntohs(sh->dest);
+		from = &skb->nh.iph->daddr;
+	}
+	memcpy(&addr->v4.sin_addr.s_addr, from, sizeof(struct in_addr));
+}
+
+/* Initialize an sctp_addr from a socket. */
+static void sctp_v4_from_sk(union sctp_addr *addr, struct sock *sk)
+{
+	addr->v4.sin_family = AF_INET;
+	addr->v4.sin_port = (sk)->num;
+	addr->v4.sin_addr.s_addr = (sk)->rcv_saddr;
+}
+
+/* Initialize sk->rcv_saddr from sctp_addr. */
+static void sctp_v4_to_sk_saddr(union sctp_addr *addr, struct sock *sk)
+{
+	(sk)->rcv_saddr = addr->v4.sin_addr.s_addr;
+}
+
+/* Initialize sk->daddr from sctp_addr. */
+static void sctp_v4_to_sk_daddr(union sctp_addr *addr, struct sock *sk)
+{
+	(sk)->daddr = addr->v4.sin_addr.s_addr;
+}
+
+/* Initialize a sctp_addr from a dst_entry. */
+static void sctp_v4_dst_saddr(union sctp_addr *saddr, struct dst_entry *dst,
+			      unsigned short port)
+{
+	struct rtable *rt = (struct rtable *)dst;
+	saddr->v4.sin_family = AF_INET;
+	saddr->v4.sin_port = port;
+	saddr->v4.sin_addr.s_addr = rt->rt_src;
+}
+
+/* Compare two addresses exactly. */
+static int sctp_v4_cmp_addr(const union sctp_addr *addr1,
+			    const union sctp_addr *addr2)
+{
+	if (addr1->sa.sa_family != addr2->sa.sa_family)
+		return 0;
+	if (addr1->v4.sin_port != addr2->v4.sin_port)
+		return 0;
+	if (addr1->v4.sin_addr.s_addr != addr2->v4.sin_addr.s_addr)
+		return 0;
+
+	return 1;
+}
+
+/* Initialize addr struct to INADDR_ANY. */
+static void sctp_v4_inaddr_any(union sctp_addr *addr, unsigned short port)
+{
+	addr->v4.sin_family = AF_INET;
+	addr->v4.sin_addr.s_addr = INADDR_ANY;
+	addr->v4.sin_port = port;
+}
+
+/* Is this a wildcard address? */
+static int sctp_v4_is_any(const union sctp_addr *addr)
+{
+	return INADDR_ANY == addr->v4.sin_addr.s_addr;
+}
+
+/* This function checks if the address is a valid address to be used for
+ * SCTP binding.
+ *
+ * Output:
+ * Return 0 - If the address is a non-unicast or an illegal address.
+ * Return 1 - If the address is a unicast.
+ */
+static int sctp_v4_addr_valid(union sctp_addr *addr)
+{
+	/* Is this a non-unicast address or a unusable SCTP address? */
+	if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr))
+		return 0;
+
+	return 1;
+}
+
+/* Should this be available for binding?   */
+static int sctp_v4_available(const union sctp_addr *addr)
+{
+	int ret = inet_addr_type(addr->v4.sin_addr.s_addr);
+
+	/* FIXME: ip_nonlocal_bind sysctl support. */
+
+	if (addr->v4.sin_addr.s_addr != INADDR_ANY && ret != RTN_LOCAL)
+		return 0;
+	return 1;
+}
+
+/* Checking the loopback, private and other address scopes as defined in
+ * RFC 1918.   The IPv4 scoping is based on the draft for SCTP IPv4
+ * scoping <draft-stewart-tsvwg-sctp-ipv4-00.txt>.
+ *
+ * Level 0 - unusable SCTP addresses
+ * Level 1 - loopback address
+ * Level 2 - link-local addresses
+ * Level 3 - private addresses.
+ * Level 4 - global addresses
+ * For INIT and INIT-ACK address list, let L be the level of
+ * of requested destination address, sender and receiver
+ * SHOULD include all of its addresses with level greater
+ * than or equal to L.
+ */
+static sctp_scope_t sctp_v4_scope(union sctp_addr *addr)
+{
+	sctp_scope_t retval;
+
+	/* Should IPv4 scoping be a sysctl configurable option
+	 * so users can turn it off (default on) for certain
+	 * unconventional networking environments?
+	 */
+
+	/* Check for unusable SCTP addresses. */
+	if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr)) {
+		retval =  SCTP_SCOPE_UNUSABLE;
+	} else if (LOOPBACK(addr->v4.sin_addr.s_addr)) {
+		retval = SCTP_SCOPE_LOOPBACK;
+	} else if (IS_IPV4_LINK_ADDRESS(&addr->v4.sin_addr.s_addr)) {
+		retval = SCTP_SCOPE_LINK;
+	} else if (IS_IPV4_PRIVATE_ADDRESS(&addr->v4.sin_addr.s_addr)) {
+		retval = SCTP_SCOPE_PRIVATE;
+	} else {
+		retval = SCTP_SCOPE_GLOBAL;
+	}
+
+	return retval;
+}
+
+/* Returns a valid dst cache entry for the given source and destination ip
+ * addresses. If an association is passed, trys to get a dst entry with a
+ * source address that matches an address in the bind address list.
+ */
+struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
+				  union sctp_addr *daddr,
+				  union sctp_addr *saddr)
+{
+	struct rtable *rt;
+	struct rt_key key;
+	sctp_bind_addr_t *bp;
+	rwlock_t *addr_lock;
+	struct sockaddr_storage_list *laddr;
+	struct list_head *pos;
+	struct dst_entry *dst = NULL;
+	union sctp_addr dst_saddr;
+	
+	memset(&key, 0x0, sizeof(struct rt_key));
+	key.dst = daddr->v4.sin_addr.s_addr;
+	
+	if (saddr)
+		key.src = saddr->v4.sin_addr.s_addr;
+
+	SCTP_DEBUG_PRINTK("%s: DST:%u.%u.%u.%u, SRC:%u.%u.%u.%u - ",
+			  __FUNCTION__, NIPQUAD(key.dst),
+			  NIPQUAD(key.src));
+
+	if (!ip_route_output_key(&rt, &key)) {
+		dst = &rt->u.dst;
+	}
+
+	/* If there is no association or if a source address is passed, no
+	 * more validation is required.
+	 */
+	if (!asoc || saddr)
+		goto out;
+
+	bp = &asoc->base.bind_addr;
+	addr_lock = &asoc->base.addr_lock;
+
+	if (dst) {
+		/* Walk through the bind address list and look for a bind
+		 * address that matches the source address of the returned dst.
+		 */
+		sctp_read_lock(addr_lock);
+		list_for_each(pos, &bp->address_list) {
+			laddr = list_entry(pos, struct sockaddr_storage_list,
+					   list);
+			sctp_v4_dst_saddr(&dst_saddr, dst, bp->port);
+			if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a))
+				goto out_unlock;
+		}
+		sctp_read_unlock(addr_lock);
+
+		/* None of the bound addresses match the source address of the
+		 * dst. So release it.
+		 */
+		dst_release(dst);
+		dst = NULL;
+	}
+
+	/* Walk through the bind address list and try to get a dst that
+	 * matches a bind address as the source address.
+	 */
+	sctp_read_lock(addr_lock);
+	list_for_each(pos, &bp->address_list) {
+		laddr = list_entry(pos, struct sockaddr_storage_list, list);
+
+		if (AF_INET == laddr->a.sa.sa_family) {
+			key.src = laddr->a.v4.sin_addr.s_addr;
+			if (!ip_route_output_key(&rt, &key)) {
+				dst = &rt->u.dst;
+				goto out_unlock;
+			}
+		}
+	}
+
+out_unlock:
+	sctp_read_unlock(addr_lock);
+out:
+	if (dst)
+		SCTP_DEBUG_PRINTK("rt_dst:%u.%u.%u.%u, rt_src:%u.%u.%u.%u\n",
+			  	  NIPQUAD(rt->rt_dst), NIPQUAD(rt->rt_src));
+	else
+		SCTP_DEBUG_PRINTK("NO ROUTE\n");
+
+	return dst;
+}
+
+/* For v4, the source address is cached in the route entry(dst). So no need
+ * to cache it separately and hence this is an empty routine.
+ */
+void sctp_v4_get_saddr(struct sctp_association *asoc,
+		       struct dst_entry *dst,
+		       union sctp_addr *daddr,
+		       union sctp_addr *saddr)
+{
+	struct rtable *rt = (struct rtable *)dst;
+
+	if (rt) {
+		saddr->v4.sin_family = AF_INET;
+		saddr->v4.sin_port = asoc->base.bind_addr.port;  
+		saddr->v4.sin_addr.s_addr = rt->rt_src; 
+	}
+}
+
+/* What interface did this skb arrive on? */
+int sctp_v4_skb_iif(const struct sk_buff *skb)
+{
+     	return ((struct rtable *)skb->dst)->rt_iif;
+}
+
+/* Create and initialize a new sk for the socket returned by accept(). */
+struct sock *sctp_v4_create_accept_sk(struct sock *sk,
+				      struct sctp_association *asoc)
+{
+	struct sock *newsk;
+	struct inet_opt *inet = inet_sk(sk);
+	struct inet_opt *newinet;
+
+	newsk = sk_alloc(PF_INET, GFP_KERNEL, sizeof(struct sock));
+	if (!newsk)
+		goto out;
+
+	sock_init_data(NULL, newsk);
+	sk_set_owner(newsk, THIS_MODULE);
+
+	newsk->type = SOCK_STREAM;
+
+	newsk->prot = sk->prot;
+	newsk->no_check = sk->no_check;
+	newsk->reuse = sk->reuse;
+	newsk->shutdown = sk->shutdown;
+
+	newsk->destruct = inet_sock_destruct;
+	newsk->zapped = 0;
+	newsk->family = PF_INET;
+	newsk->protocol = IPPROTO_SCTP;
+	newsk->backlog_rcv = sk->prot->backlog_rcv;
+
+	newinet = inet_sk(newsk);
+
+	/* Initialize sk's sport, dport, rcv_saddr and daddr for
+	 * getsockname() and getpeername()
+	 */
+	newsk->sport = sk->sport;
+	newsk->saddr = sk->saddr;
+	newsk->rcv_saddr = sk->rcv_saddr;
+	newsk->dport = htons(asoc->peer.port);
+	newsk->daddr = asoc->peer.primary_addr.v4.sin_addr.s_addr;
+	newinet->pmtudisc = inet->pmtudisc;
+      	newinet->id = 0;
+
+	newinet->ttl = sysctl_ip_default_ttl;
+	newinet->mc_loop = 1;
+	newinet->mc_ttl = 1;
+	newinet->mc_index = 0;
+	newinet->mc_list = NULL;
+
+#ifdef INET_REFCNT_DEBUG
+	atomic_inc(&inet_sock_nr);
+#endif
+
+	if (0 != newsk->prot->init(newsk)) {
+		inet_sock_release(newsk);
+		newsk = NULL;
+	}
+
+out:
+	return newsk;
+}
+
+/* Event handler for inet address addition/deletion events.
+ * Basically, whenever there is an event, we re-build our local address list.
+ */
+static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev,
+			       void *ptr)
+{
+	unsigned long flags;
+
+	sctp_spin_lock_irqsave(&sctp_proto.local_addr_lock, flags);
+	__sctp_free_local_addr_list(&sctp_proto);
+	__sctp_get_local_addr_list(&sctp_proto);
+	sctp_spin_unlock_irqrestore(&sctp_proto.local_addr_lock, flags);
+
+	return NOTIFY_DONE;
+}
+
+/*
+ * Initialize the control inode/socket with a control endpoint data
+ * structure.  This endpoint is reserved exclusively for the OOTB processing.
+ */
+int sctp_ctl_sock_init(void)
+{
+	int err;
+	sa_family_t family;
+
+	if (sctp_get_pf_specific(PF_INET6))
+		family = PF_INET6;
+	else
+		family = PF_INET;
+
+	err = sock_create(family, SOCK_SEQPACKET, IPPROTO_SCTP,
+			  &sctp_ctl_socket);
+	if (err < 0) {
+		printk(KERN_ERR
+		       "SCTP: Failed to create the SCTP control socket.\n");
+		return err;
+	}
+	sctp_ctl_socket->sk->allocation = GFP_ATOMIC;
+	inet_sk(sctp_ctl_socket->sk)->ttl = MAXTTL;
+
+	return 0;
+}
+
+/* Register address family specific functions. */
+int sctp_register_af(struct sctp_af *af)
+{
+	switch (af->sa_family) {
+	case AF_INET:
+		if (sctp_af_v4_specific)
+			return 0;
+		sctp_af_v4_specific = af;
+		break;
+	case AF_INET6:
+		if (sctp_af_v6_specific)
+			return 0;
+		sctp_af_v6_specific = af;
+		break;
+	default:
+		return 0;
+	}
+
+	INIT_LIST_HEAD(&af->list);
+	list_add_tail(&af->list, &sctp_proto.address_families);
+	return 1;
+}
+
+/* Get the table of functions for manipulating a particular address
+ * family.
+ */
+struct sctp_af *sctp_get_af_specific(sa_family_t family)
+{
+	switch (family) {
+	case AF_INET:
+		return sctp_af_v4_specific;
+	case AF_INET6:
+		return sctp_af_v6_specific;
+	default:
+		return NULL;
+	}
+}
+
+/* Common code to initialize a AF_INET msg_name. */
+static void sctp_inet_msgname(char *msgname, int *addr_len)
+{
+	struct sockaddr_in *sin;
+
+	sin = (struct sockaddr_in *)msgname;
+	*addr_len = sizeof(struct sockaddr_in);
+	sin->sin_family = AF_INET;
+	memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+}
+
+/* Copy the primary address of the peer primary address as the msg_name. */
+static void sctp_inet_event_msgname(struct sctp_ulpevent *event, char *msgname,
+				    int *addr_len)
+{
+	struct sockaddr_in *sin, *sinfrom;
+
+	if (msgname) {
+		sctp_inet_msgname(msgname, addr_len);
+		sin = (struct sockaddr_in *)msgname;
+		sinfrom = &event->event_asoc->peer.primary_addr.v4;
+		sin->sin_port = htons(event->event_asoc->peer.port);
+		sin->sin_addr.s_addr = sinfrom->sin_addr.s_addr;
+	}
+}
+
+/* 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) {
+		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;
+	}
+}
+
+/* Do we support this AF? */
+static int sctp_inet_af_supported(sa_family_t family)
+{
+	/* PF_INET only supports AF_INET addresses. */
+	return (AF_INET == family);
+}
+
+/* Address matching with wildcards allowed. */
+static int sctp_inet_cmp_addr(const union sctp_addr *addr1,
+			      const union sctp_addr *addr2,
+			      struct sctp_opt *opt)
+{
+	/* PF_INET only supports AF_INET addresses. */
+	if (addr1->sa.sa_family != addr2->sa.sa_family)
+		return 0;
+	if (INADDR_ANY == addr1->v4.sin_addr.s_addr ||
+	    INADDR_ANY == addr2->v4.sin_addr.s_addr)
+		return 1;
+	if (addr1->v4.sin_addr.s_addr == addr2->v4.sin_addr.s_addr)
+		return 1;
+
+	return 0;
+}
+
+/* Verify that provided sockaddr looks bindable.  Common verification has
+ * already been taken care of.
+ */
+static int sctp_inet_bind_verify(struct sctp_opt *opt, union sctp_addr *addr)
+{
+	return sctp_v4_available(addr);
+}
+
+/* Verify that sockaddr looks sendable.  Common verification has already
+ * been taken care of.
+ */
+static int sctp_inet_send_verify(struct sctp_opt *opt, union sctp_addr *addr)
+{
+	return 1;
+}
+
+/* Fill in Supported Address Type information for INIT and INIT-ACK
+ * chunks.  Returns number of addresses supported.
+ */
+static int sctp_inet_supported_addrs(const struct sctp_opt *opt,
+				     __u16 *types)
+{
+	types[0] = SCTP_PARAM_IPV4_ADDRESS;
+	return 1;
+}
+
+/* Wrapper routine that calls the ip transmit routine. */
+static inline int sctp_v4_xmit(struct sk_buff *skb,
+			       struct sctp_transport *transport, int ipfragok)
+{
+	SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, "
+			  "src:%u.%u.%u.%u, dst:%u.%u.%u.%u\n",
+			  __FUNCTION__, skb, skb->len,
+			  NIPQUAD(((struct rtable *)skb->dst)->rt_src),
+			  NIPQUAD(((struct rtable *)skb->dst)->rt_dst));
+
+	SCTP_INC_STATS(SctpOutSCTPPacks);
+	return ip_queue_xmit(skb, ipfragok);
+}
+
+struct sctp_af sctp_ipv4_specific;
+
+static struct sctp_pf sctp_pf_inet = {
+	.event_msgname = sctp_inet_event_msgname,
+	.skb_msgname   = sctp_inet_skb_msgname,
+	.af_supported  = sctp_inet_af_supported,
+	.cmp_addr      = sctp_inet_cmp_addr,
+	.bind_verify   = sctp_inet_bind_verify,
+	.send_verify   = sctp_inet_send_verify,
+	.supported_addrs = sctp_inet_supported_addrs,
+	.create_accept_sk = sctp_v4_create_accept_sk,
+	.af            = &sctp_ipv4_specific,
+};
+
+/* Notifier for inetaddr addition/deletion events.  */
+struct notifier_block sctp_inetaddr_notifier = {
+	.notifier_call = sctp_inetaddr_event,
+};
+
+/* Socket operations.  */
+struct proto_ops inet_seqpacket_ops = {
+	.family      = PF_INET,
+	.release     = inet_release,       /* Needs to be wrapped... */
+	.bind        = inet_bind,
+	.connect     = inet_dgram_connect,
+	.socketpair  = sock_no_socketpair,
+	.accept      = inet_accept,
+	.getname     = inet_getname,      /* Semantics are different.  */
+	.poll        = sctp_poll,
+	.ioctl       = inet_ioctl,
+	.listen      = sctp_inet_listen,
+	.shutdown    = inet_shutdown,     /* Looks harmless.  */
+	.setsockopt  = inet_setsockopt,   /* IP_SOL IP_OPTION is a problem. */
+	.getsockopt  = inet_getsockopt,
+	.sendmsg     = inet_sendmsg,
+	.recvmsg     = inet_recvmsg,
+	.mmap        = sock_no_mmap,
+	.sendpage    = sock_no_sendpage,
+};
+
+/* Registration with AF_INET family.  */
+static struct inet_protosw sctp_seqpacket_protosw = {
+	.type       = SOCK_SEQPACKET,
+	.protocol   = IPPROTO_SCTP,
+	.prot       = &sctp_prot,
+	.ops        = &inet_seqpacket_ops,
+	.capability = -1,
+	.no_check   = 0,
+	.flags      = SCTP_PROTOSW_FLAG
+};
+static struct inet_protosw sctp_stream_protosw = {
+	.type       = SOCK_STREAM,
+	.protocol   = IPPROTO_SCTP,
+	.prot       = &sctp_prot,
+	.ops        = &inet_seqpacket_ops,
+	.capability = -1,
+	.no_check   = 0,
+	.flags      = SCTP_PROTOSW_FLAG
+};
+
+/* Register with IP layer.  */
+static struct inet_protocol sctp_protocol = {
+	.handler     = sctp_rcv,
+	.err_handler = sctp_v4_err,
+	.protocol    = IPPROTO_SCTP,
+	.name        = "SCTP"
+};
+
+/* IPv4 address related functions.  */
+struct sctp_af sctp_ipv4_specific = {
+	.sctp_xmit      = sctp_v4_xmit,
+	.setsockopt     = ip_setsockopt,
+	.getsockopt     = ip_getsockopt,
+	.get_dst	= sctp_v4_get_dst,
+	.get_saddr	= sctp_v4_get_saddr,
+	.copy_addrlist  = sctp_v4_copy_addrlist,
+	.from_skb       = sctp_v4_from_skb,
+	.from_sk        = sctp_v4_from_sk,
+	.to_sk_saddr    = sctp_v4_to_sk_saddr,
+	.to_sk_daddr    = sctp_v4_to_sk_daddr,
+	.dst_saddr      = sctp_v4_dst_saddr,
+	.cmp_addr       = sctp_v4_cmp_addr,
+	.addr_valid     = sctp_v4_addr_valid,
+	.inaddr_any     = sctp_v4_inaddr_any,
+	.is_any         = sctp_v4_is_any,
+	.available      = sctp_v4_available,
+	.scope          = sctp_v4_scope,
+	.skb_iif        = sctp_v4_skb_iif,
+	.net_header_len = sizeof(struct iphdr),
+	.sockaddr_len   = sizeof(struct sockaddr_in),
+	.sa_family      = AF_INET,
+};
+
+struct sctp_pf *sctp_get_pf_specific(sa_family_t family) {
+
+	switch (family) {
+	case PF_INET:
+		return sctp_pf_inet_specific;
+	case PF_INET6:
+		return sctp_pf_inet6_specific;
+	default:
+		return NULL;
+	}
+}
+
+/* Register the PF specific function table.  */
+int sctp_register_pf(struct sctp_pf *pf, sa_family_t family)
+{
+	switch (family) {
+	case PF_INET:
+		if (sctp_pf_inet_specific)
+			return 0;
+		sctp_pf_inet_specific = pf;
+		break;
+	case PF_INET6:
+		if (sctp_pf_inet6_specific)
+			return 0;
+		sctp_pf_inet6_specific = pf;
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static int __init init_sctp_mibs(void)
+{
+	return 0;
+}
+
+static void cleanup_sctp_mibs(void)
+{
+}
+
+/* Initialize the universe into something sensible.  */
+__init int sctp_init(void)
+{
+	int i;
+	int status = 0;
+
+	/* SCTP_DEBUG sanity check. */
+	if (!sctp_sanity_check())
+		return -EINVAL;
+
+	/* Add SCTP to inet_protos hash table.  */
+	inet_add_protocol(&sctp_protocol);
+
+	/* Add SCTP(TCP and UDP style) to inetsw linked list.  */
+	inet_register_protosw(&sctp_seqpacket_protosw);
+	inet_register_protosw(&sctp_stream_protosw);
+
+	/* Allocate and initialise sctp mibs.  */
+	status = init_sctp_mibs();
+	if (status)
+		goto err_init_mibs;
+
+	/* Initialize proc fs directory.  */
+	sctp_proc_init();
+
+	/* Initialize object count debugging.  */
+	sctp_dbg_objcnt_init();
+
+	/* Initialize the SCTP specific PF functions. */
+	sctp_register_pf(&sctp_pf_inet, PF_INET);
+	/*
+	 * 14. Suggested SCTP Protocol Parameter Values
+	 */
+	/* The following protocol parameters are RECOMMENDED:  */
+	/* RTO.Initial              - 3  seconds */
+	sctp_proto.rto_initial		= SCTP_RTO_INITIAL;
+	/* RTO.Min                  - 1  second */
+	sctp_proto.rto_min	 	= SCTP_RTO_MIN;
+	/* RTO.Max                 -  60 seconds */
+	sctp_proto.rto_max 		= SCTP_RTO_MAX;
+	/* RTO.Alpha                - 1/8 */
+	sctp_proto.rto_alpha	        = SCTP_RTO_ALPHA;
+	/* RTO.Beta                 - 1/4 */
+	sctp_proto.rto_beta		= SCTP_RTO_BETA;
+
+	/* Valid.Cookie.Life        - 60  seconds */
+	sctp_proto.valid_cookie_life	= 60 * HZ;
+
+	/* Whether Cookie Preservative is enabled(1) or not(0) */
+	sctp_proto.cookie_preserve_enable = 1;
+
+	/* Max.Burst		    - 4 */
+	sctp_proto.max_burst = SCTP_MAX_BURST;
+
+	/* Association.Max.Retrans  - 10 attempts
+	 * Path.Max.Retrans         - 5  attempts (per destination address)
+	 * Max.Init.Retransmits     - 8  attempts
+	 */
+	sctp_proto.max_retrans_association = 10;
+	sctp_proto.max_retrans_path	= 5;
+	sctp_proto.max_retrans_init	= 8;
+
+	/* HB.interval              - 30 seconds */
+	sctp_proto.hb_interval		= 30 * HZ;
+
+	/* Implementation specific variables. */
+
+	/* Initialize default stream count setup information. */
+	sctp_proto.max_instreams    = SCTP_DEFAULT_INSTREAMS;
+	sctp_proto.max_outstreams   = SCTP_DEFAULT_OUTSTREAMS;
+
+	/* Allocate and initialize the association hash table.  */
+	sctp_proto.assoc_hashsize = 4096;
+	sctp_proto.assoc_hashbucket = (sctp_hashbucket_t *)
+		kmalloc(4096 * sizeof(sctp_hashbucket_t), GFP_KERNEL);
+	if (!sctp_proto.assoc_hashbucket) {
+		printk(KERN_ERR "SCTP: Failed association hash alloc.\n");
+		status = -ENOMEM;
+		goto err_ahash_alloc;
+	}
+	for (i = 0; i < sctp_proto.assoc_hashsize; i++) {
+		sctp_proto.assoc_hashbucket[i].lock = RW_LOCK_UNLOCKED;
+		sctp_proto.assoc_hashbucket[i].chain = NULL;
+	}
+
+	/* Allocate and initialize the endpoint hash table.  */
+	sctp_proto.ep_hashsize = 64;
+	sctp_proto.ep_hashbucket = (sctp_hashbucket_t *)
+		kmalloc(64 * sizeof(sctp_hashbucket_t), GFP_KERNEL);
+	if (!sctp_proto.ep_hashbucket) {
+		printk(KERN_ERR "SCTP: Failed endpoint_hash alloc.\n");
+		status = -ENOMEM;
+		goto err_ehash_alloc;
+	}
+
+	for (i = 0; i < sctp_proto.ep_hashsize; i++) {
+		sctp_proto.ep_hashbucket[i].lock = RW_LOCK_UNLOCKED;
+		sctp_proto.ep_hashbucket[i].chain = NULL;
+	}
+
+	/* Allocate and initialize the SCTP port hash table.  */
+	sctp_proto.port_hashsize = 4096;
+	sctp_proto.port_hashtable = (sctp_bind_hashbucket_t *)
+		kmalloc(4096 * sizeof(sctp_bind_hashbucket_t), GFP_KERNEL);
+	if (!sctp_proto.port_hashtable) {
+		printk(KERN_ERR "SCTP: Failed bind hash alloc.");
+		status = -ENOMEM;
+		goto err_bhash_alloc;
+	}
+
+	sctp_proto.port_alloc_lock = SPIN_LOCK_UNLOCKED;
+	sctp_proto.port_rover = sysctl_local_port_range[0] - 1;
+	for (i = 0; i < sctp_proto.port_hashsize; i++) {
+		sctp_proto.port_hashtable[i].lock = SPIN_LOCK_UNLOCKED;
+		sctp_proto.port_hashtable[i].chain = NULL;
+	}
+
+	sctp_sysctl_register();
+
+	INIT_LIST_HEAD(&sctp_proto.address_families);
+	sctp_register_af(&sctp_ipv4_specific);
+
+	status = sctp_v6_init();
+	if (status)
+		goto err_v6_init;
+
+	/* Initialize the control inode/socket for handling OOTB packets.  */
+	if ((status = sctp_ctl_sock_init())) {
+		printk (KERN_ERR
+			"SCTP: Failed to initialize the SCTP control sock.\n");
+		goto err_ctl_sock_init;
+	}
+
+	/* Initialize the local address list. */
+	INIT_LIST_HEAD(&sctp_proto.local_addr_list);
+	sctp_proto.local_addr_lock = SPIN_LOCK_UNLOCKED;
+
+	/* Register notifier for inet address additions/deletions. */
+	register_inetaddr_notifier(&sctp_inetaddr_notifier);
+
+	sctp_get_local_addr_list(&sctp_proto);
+
+	__unsafe(THIS_MODULE);
+	return 0;
+
+err_ctl_sock_init:
+	sctp_v6_exit();
+err_v6_init:
+	sctp_sysctl_unregister();
+	list_del(&sctp_ipv4_specific.list);
+	kfree(sctp_proto.port_hashtable);
+err_bhash_alloc:
+	kfree(sctp_proto.ep_hashbucket);
+err_ehash_alloc:
+	kfree(sctp_proto.assoc_hashbucket);
+err_ahash_alloc:
+	sctp_dbg_objcnt_exit();
+	sctp_proc_exit();
+	cleanup_sctp_mibs();
+err_init_mibs:
+	inet_del_protocol(&sctp_protocol);
+	inet_unregister_protosw(&sctp_seqpacket_protosw);
+	inet_unregister_protosw(&sctp_stream_protosw);
+	return status;
+}
+
+/* Exit handler for the SCTP protocol.  */
+__exit void sctp_exit(void)
+{
+	/* BUG.  This should probably do something useful like clean
+	 * up all the remaining associations and all that memory.
+	 */
+
+	/* Unregister notifier for inet address additions/deletions. */
+	unregister_inetaddr_notifier(&sctp_inetaddr_notifier);
+
+	/* Free the local address list.  */
+	sctp_free_local_addr_list(&sctp_proto);
+
+	/* Free the control endpoint.  */
+	sock_release(sctp_ctl_socket);
+
+	sctp_v6_exit();
+	sctp_sysctl_unregister();
+	list_del(&sctp_ipv4_specific.list);
+
+	kfree(sctp_proto.assoc_hashbucket);
+	kfree(sctp_proto.ep_hashbucket);
+	kfree(sctp_proto.port_hashtable);
+
+	sctp_dbg_objcnt_exit();
+	sctp_proc_exit();
+	cleanup_sctp_mibs();
+
+	inet_del_protocol(&sctp_protocol);
+	inet_unregister_protosw(&sctp_seqpacket_protosw);
+	inet_unregister_protosw(&sctp_stream_protosw);
+}
+
+module_init(sctp_init);
+module_exit(sctp_exit);
+
+MODULE_AUTHOR("Linux Kernel SCTP developers <lksctp-developers@lists.sourceforge.net>");
+MODULE_DESCRIPTION("Support for the SCTP protocol (RFC2960)");
+MODULE_LICENSE("GPL");
diff -urN linux-2.4.23-pre5/net/sctp/sla1.c linux-2.4.23-pre6/net/sctp/sla1.c
--- linux-2.4.23-pre5/net/sctp/sla1.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/sla1.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,281 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * 
+ * This file is part of the SCTP kernel reference Implementation
+ * 
+ * (It's really SHA-1 but Hey I was tired when I created this 
+ * file, and on a plane to France :-)
+ * 
+ * The SCTP reference implementation is free software; 
+ * you can redistribute it and/or modify it under the terms of 
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ * 
+ * The SCTP reference implementation is distributed in the hope that it 
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.  
+ * 
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ * 
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by: 
+ *    Randall Stewart <rstewar1@email.mot.com>
+ *    kmorneau@cisco.com
+ *    qxie1@email.mot.com
+ *
+ * Based on:
+ *    Randy Stewart, et al. SCTP Reference Implementation which is licenced
+ *    under the GPL. 
+ * 
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fcntl.h>
+#include <asm/string.h>		/* for memcpy */
+#include <linux/sched.h>	/* dead chicken for in.h */
+#include <linux/in.h>		/* for htonl and ntohl */
+
+#include <net/sctp/sla1.h>
+
+void SLA1_Init(struct SLA_1_Context *ctx)
+{
+	/* Init the SLA-1 context structure.  */
+	ctx->A = 0;
+	ctx->B = 0;
+	ctx->C = 0;
+	ctx->D = 0;
+	ctx->E = 0;
+	ctx->H0 = H0INIT;
+	ctx->H1 = H1INIT;
+	ctx->H2 = H2INIT;
+	ctx->H3 = H3INIT;
+	ctx->H4 = H4INIT;
+	ctx->TEMP = 0;
+	memset(ctx->words, 0, sizeof(ctx->words));
+	ctx->howManyInBlock = 0;
+	ctx->runningTotal = 0;
+}
+
+void SLA1processABlock(struct SLA_1_Context *ctx,unsigned int *block)
+{
+	int i;
+	/* init the W0-W15 to the block of words being
+	 * hashed.
+	 */
+	/* step a) */
+
+	for (i = 0; i < 16; i++)
+		ctx->words[i] = ntohl(block[i]);
+
+	/* now init the rest based on the SLA-1 formula, step b) */
+	for (i = 16; i < 80; i++)
+		ctx->words[i] =
+			CSHIFT(1, ((ctx->words[(i-3)]) ^
+				   (ctx->words[(i-8)]) ^
+				   (ctx->words[(i-14)]) ^
+				   (ctx->words[(i-16)])));
+
+	/* step c) */
+	ctx->A = ctx->H0;
+	ctx->B = ctx->H1;
+	ctx->C = ctx->H2;
+	ctx->D = ctx->H3;
+	ctx->E = ctx->H4;
+
+	/* step d) */
+	for (i = 0; i < 80; i++) {
+		if (i < 20) {
+			ctx->TEMP = ((CSHIFT(5, ctx->A)) +
+				     (F1(ctx->B, ctx->C, ctx->D)) +
+				     (ctx->E) +
+				     ctx->words[i] +
+				     K1
+				);
+		} else if (i < 40) {
+			ctx->TEMP = ((CSHIFT(5, ctx->A)) +
+				     (F2(ctx->B, ctx->C, ctx->D)) +
+				     (ctx->E) +
+				     (ctx->words[i]) +
+				     K2
+				);
+		} else if (i < 60) {
+			ctx->TEMP = ((CSHIFT(5, ctx->A)) +
+				     (F3(ctx->B, ctx->C, ctx->D)) +
+				     (ctx->E) +
+				     (ctx->words[i]) +
+				     K3
+				);
+		} else {
+			ctx->TEMP = ((CSHIFT(5, ctx->A)) +
+				     (F4(ctx->B, ctx->C, ctx->D)) +
+				     (ctx->E) +
+				     (ctx->words[i]) +
+				     K4
+				);
+		}
+		ctx->E = ctx->D;
+		ctx->D = ctx->C;
+		ctx->C = CSHIFT(30, ctx->B);
+		ctx->B = ctx->A;
+		ctx->A = ctx->TEMP;
+	}
+
+	/* step e) */
+	ctx->H0 = (ctx->H0) + (ctx->A);
+	ctx->H1 = (ctx->H1) + (ctx->B);
+	ctx->H2 = (ctx->H2) + (ctx->C);
+	ctx->H3 = (ctx->H3) + (ctx->D);
+	ctx->H4 = (ctx->H4) + (ctx->E);
+}
+
+void SLA1_Process(struct SLA_1_Context *ctx, const unsigned char *ptr, int siz)
+{
+	int numberLeft, leftToFill;
+
+	numberLeft = siz;
+	while (numberLeft > 0) {
+		leftToFill = sizeof(ctx->SLAblock) - ctx->howManyInBlock;
+		if (leftToFill > numberLeft) {
+			/* can only partially fill up this one */
+			memcpy(&ctx->SLAblock[ctx->howManyInBlock],
+			       ptr, numberLeft);
+			ctx->howManyInBlock += siz;
+			ctx->runningTotal += siz;
+			break;
+		} else {
+			/* block is now full, process it */
+			memcpy(&ctx->SLAblock[ctx->howManyInBlock],
+			       ptr, leftToFill);
+			SLA1processABlock(ctx, (unsigned int *) ctx->SLAblock);
+			numberLeft -= leftToFill;
+			ctx->runningTotal += leftToFill;
+			ctx->howManyInBlock = 0;
+		}
+	}
+}
+
+void SLA1_Final(struct SLA_1_Context *ctx, unsigned char *digestBuf)
+{
+	/* if any left in block fill with padding
+	 * and process. Then transfer the digest to
+	 * the pointer. At the last block some special
+	 * rules need to apply. We must add a 1 bit
+	 * following the message, then we pad with
+	 * 0's. The total size is encoded as a 64 bit
+	 * number at the end. Now if the last buffer has
+	 * more than 55 octets in it we cannot fit
+	 * the 64 bit number + 10000000 pad on the end
+	 * and must add the 10000000 pad, pad the rest
+	 * of the message with 0's and then create a
+	 * all 0 message with just the 64 bit size
+	 * at the end and run this block through by itself.
+	 * Also the 64 bit int must be in network byte
+	 * order.
+	 */
+	int i, leftToFill;
+	unsigned int *ptr;
+
+	if (ctx->howManyInBlock > 55) {
+		/* special case, we need to process two
+		 * blocks here. One for the current stuff
+		 * plus possibly the pad. The other for
+		 * the size.
+		 */
+		leftToFill = sizeof(ctx->SLAblock) - ctx->howManyInBlock;
+		if (leftToFill == 0) {
+			/* Should not really happen but I am paranoid */
+			/* Not paranoid enough!  It is possible for leftToFill
+			 * to become negative!  AAA!!!!  This is another reason
+			 * to pick MD5 :-)...
+			 */
+			SLA1processABlock(ctx, (unsigned int *) ctx->SLAblock);
+			/* init last block, a bit different then the rest :-) */
+			ctx->SLAblock[0] = 0x80;
+			for (i = 1; i < sizeof(ctx->SLAblock); i++) {
+				ctx->SLAblock[i] = 0x0;
+			}
+		} else if (leftToFill == 1) {
+			ctx->SLAblock[ctx->howManyInBlock] = 0x80;
+			SLA1processABlock(ctx, (unsigned int *) ctx->SLAblock);
+			/* init last block */
+			memset(ctx->SLAblock, 0, sizeof(ctx->SLAblock));
+		} else {
+			ctx->SLAblock[ctx->howManyInBlock] = 0x80;
+			for (i = (ctx->howManyInBlock + 1);
+			     i < sizeof(ctx->SLAblock);
+			     i++) {
+				ctx->SLAblock[i] = 0x0;
+			}
+			SLA1processABlock(ctx, (unsigned int *) ctx->SLAblock);
+			/* init last block */
+			memset(ctx->SLAblock, 0, sizeof(ctx->SLAblock));
+		}
+		/* This is in bits so multiply by 8 */
+		ctx->runningTotal *= 8;
+		ptr = (unsigned int *) &ctx->SLAblock[60];
+		*ptr = htonl(ctx->runningTotal);
+		SLA1processABlock(ctx, (unsigned int *) ctx->SLAblock);
+	} else {
+		/* easy case, we just pad this
+		 * message to size - end with 0
+		 * add the magic 0x80 to the next
+		 * word and then put the network byte
+		 * order size in the last spot and
+		 * process the block.
+		 */
+		ctx->SLAblock[ctx->howManyInBlock] = 0x80;
+		for (i = (ctx->howManyInBlock + 1);
+		     i < sizeof(ctx->SLAblock);
+		     i++) {
+			ctx->SLAblock[i] = 0x0;
+		}
+		/* get last int spot */
+		ctx->runningTotal *= 8;
+		ptr = (unsigned int *) &ctx->SLAblock[60];
+		*ptr = htonl(ctx->runningTotal);
+		SLA1processABlock(ctx, (unsigned int *) ctx->SLAblock);
+	}
+	/* Now at this point all we need do is transfer the
+	 * digest back to the user
+	 */
+	digestBuf[3] = (ctx->H0 & 0xff);
+	digestBuf[2] = ((ctx->H0 >> 8) & 0xff);
+	digestBuf[1] = ((ctx->H0 >> 16) & 0xff);
+	digestBuf[0] = ((ctx->H0 >> 24) & 0xff);
+
+	digestBuf[7] = (ctx->H1 & 0xff);
+	digestBuf[6] = ((ctx->H1 >> 8) & 0xff);
+	digestBuf[5] = ((ctx->H1 >> 16) & 0xff);
+	digestBuf[4] = ((ctx->H1 >> 24) & 0xff);
+
+	digestBuf[11] = (ctx->H2 & 0xff);
+	digestBuf[10] = ((ctx->H2 >> 8) & 0xff);
+	digestBuf[9] = ((ctx->H2 >> 16) & 0xff);
+	digestBuf[8] = ((ctx->H2 >> 24) & 0xff);
+
+	digestBuf[15] = (ctx->H3 & 0xff);
+	digestBuf[14] = ((ctx->H3 >> 8) & 0xff);
+	digestBuf[13] = ((ctx->H3 >> 16) & 0xff);
+	digestBuf[12] = ((ctx->H3 >> 24) & 0xff);
+
+	digestBuf[19] = (ctx->H4 & 0xff);
+	digestBuf[18] = ((ctx->H4 >> 8) & 0xff);
+	digestBuf[17] = ((ctx->H4 >> 16) & 0xff);
+	digestBuf[16] = ((ctx->H4 >> 24) & 0xff);
+}
diff -urN linux-2.4.23-pre5/net/sctp/sm_make_chunk.c linux-2.4.23-pre6/net/sctp/sm_make_chunk.c
--- linux-2.4.23-pre5/net/sctp/sm_make_chunk.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/sm_make_chunk.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,2199 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001-2002 Intel Corp.
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * This file includes part of the implementation of the add-IP extension,
+ * based on <draft-ietf-tsvwg-addip-sctp-02.txt> June 29, 2001,
+ * for the SCTP kernel reference Implementation.
+ *
+ * These functions work with the state functions in sctp_sm_statefuns.c
+ * to implement the state operations.  These functions implement the
+ * steps which require modifying existing data structures.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Karl Knutson          <karl@athena.chicago.il.us>
+ *    C. Robin              <chris@hundredacre.ac.uk>
+ *    Jon Grimm             <jgrimm@us.ibm.com>
+ *    Xingang Guo           <xingang.guo@intel.com>
+ *    Dajiang Zhang	    <dajiang.zhang@nokia.com>
+ *    Sridhar Samudrala	    <sri@us.ibm.com>
+ *    Daisy Chang	    <daisyc@us.ibm.com>
+ *    Ardelle Fan	    <ardelle.fan@intel.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <asm/scatterlist.h>
+#include <net/sock.h>
+
+#include <linux/skbuff.h>
+#include <linux/random.h>	/* for get_random_bytes */
+#include <net/sctp/sctp.h>
+#include <net/sctp/sm.h>
+
+/* What was the inbound interface for this chunk? */
+int sctp_chunk_iif(const struct sctp_chunk *chunk)
+{
+	struct sctp_af *af;
+	int iif = 0;
+
+	af = sctp_get_af_specific(ipver2af(chunk->skb->nh.iph->version));
+	if (af)
+		iif = af->skb_iif(chunk->skb);
+
+	return iif;
+}
+
+/* RFC 2960 3.3.2 Initiation (INIT) (1)
+ *
+ * Note 2: The ECN capable field is reserved for future use of
+ * Explicit Congestion Notification.
+ */
+static const sctp_ecn_capable_param_t ecap_param = {
+	{
+		SCTP_PARAM_ECN_CAPABLE,
+		__constant_htons(sizeof(sctp_ecn_capable_param_t)),
+	}
+};
+
+/* A helper to initialize to initialize an op error inside a
+ * provided chunk, as most cause codes will be embedded inside an
+ * abort chunk.
+ */
+void  sctp_init_cause(sctp_chunk_t *chunk, __u16 cause_code,
+		      const void *payload, size_t paylen)
+{
+	sctp_errhdr_t err;
+	int padlen;
+	__u16 len;
+
+        /* Cause code constants are now defined in network order.  */
+	err.cause = cause_code;
+	len = sizeof(sctp_errhdr_t) + paylen;
+	padlen = len % 4;
+	err.length  = htons(len);
+	len += padlen;
+	sctp_addto_chunk(chunk, sizeof(sctp_errhdr_t), &err);
+	chunk->subh.err_hdr = sctp_addto_chunk(chunk, paylen, payload);
+}
+
+/* 3.3.2 Initiation (INIT) (1)
+ *
+ * This chunk is used to initiate a SCTP association between two
+ * endpoints. The format of the INIT chunk is shown below:
+ *
+ *     0                   1                   2                   3
+ *     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *    |   Type = 1    |  Chunk Flags  |      Chunk Length             |
+ *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *    |                         Initiate Tag                          |
+ *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *    |           Advertised Receiver Window Credit (a_rwnd)          |
+ *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *    |  Number of Outbound Streams   |  Number of Inbound Streams    |
+ *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *    |                          Initial TSN                          |
+ *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *    \                                                               \
+ *    /              Optional/Variable-Length Parameters              /
+ *    \                                                               \
+ *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *
+ * The INIT chunk contains the following parameters. Unless otherwise
+ * noted, each parameter MUST only be included once in the INIT chunk.
+ *
+ * Fixed Parameters                     Status
+ * ----------------------------------------------
+ * Initiate Tag                        Mandatory
+ * Advertised Receiver Window Credit   Mandatory
+ * Number of Outbound Streams          Mandatory
+ * Number of Inbound Streams           Mandatory
+ * Initial TSN                         Mandatory
+ *
+ * Variable Parameters                  Status     Type Value
+ * -------------------------------------------------------------
+ * IPv4 Address (Note 1)               Optional    5
+ * IPv6 Address (Note 1)               Optional    6
+ * Cookie Preservative                 Optional    9
+ * Reserved for ECN Capable (Note 2)   Optional    32768 (0x8000)
+ * Host Name Address (Note 3)          Optional    11
+ * Supported Address Types (Note 4)    Optional    12
+ */
+sctp_chunk_t *sctp_make_init(const struct sctp_association *asoc,
+			     const sctp_bind_addr_t *bp,
+			     int gfp, int vparam_len)
+{
+	sctp_inithdr_t init;
+	union sctp_params addrs;
+	size_t chunksize;
+	sctp_chunk_t *retval = NULL;
+	int num_types, addrs_len = 0;
+	struct sctp_opt *sp;
+	sctp_supported_addrs_param_t sat;
+	__u16 types[2];
+
+	/* RFC 2960 3.3.2 Initiation (INIT) (1)
+	 *
+	 * Note 1: The INIT chunks can contain multiple addresses that
+	 * can be IPv4 and/or IPv6 in any combination.
+	 */
+	retval = NULL;
+
+	/* Convert the provided bind address list to raw format. */
+	addrs = sctp_bind_addrs_to_raw(bp, &addrs_len, gfp);
+
+	init.init_tag		   = htonl(asoc->c.my_vtag);
+	init.a_rwnd		   = htonl(asoc->rwnd);
+	init.num_outbound_streams  = htons(asoc->c.sinit_num_ostreams);
+	init.num_inbound_streams   = htons(asoc->c.sinit_max_instreams);
+	init.initial_tsn	   = htonl(asoc->c.initial_tsn);
+
+	/* How many address types are needed? */
+	sp = sctp_sk(asoc->base.sk);
+	num_types = sp->pf->supported_addrs(sp, types);
+
+	chunksize = sizeof(init) + addrs_len + SCTP_SAT_LEN(num_types);
+	chunksize += sizeof(ecap_param);
+	chunksize += vparam_len;
+
+	/* RFC 2960 3.3.2 Initiation (INIT) (1)
+	 *
+	 * Note 3: An INIT chunk MUST NOT contain more than one Host
+	 * Name address parameter. Moreover, the sender of the INIT
+	 * MUST NOT combine any other address types with the Host Name
+	 * address in the INIT. The receiver of INIT MUST ignore any
+	 * other address types if the Host Name address parameter is
+	 * present in the received INIT chunk.
+	 *
+	 * PLEASE DO NOT FIXME [This version does not support Host Name.]
+	 */
+
+	retval = sctp_make_chunk(asoc, SCTP_CID_INIT, 0, chunksize);
+	if (!retval)
+		goto nodata;
+
+	retval->subh.init_hdr =
+		sctp_addto_chunk(retval, sizeof(init), &init);
+	retval->param_hdr.v =
+		sctp_addto_chunk(retval, addrs_len, addrs.v);
+
+	/* RFC 2960 3.3.2 Initiation (INIT) (1)
+	 *
+	 * Note 4: This parameter, when present, specifies all the
+	 * address types the sending endpoint can support. The absence
+	 * of this parameter indicates that the sending endpoint can
+	 * support any address type.
+	 */
+	sat.param_hdr.type = SCTP_PARAM_SUPPORTED_ADDRESS_TYPES;
+	sat.param_hdr.length = htons(SCTP_SAT_LEN(num_types));
+	sctp_addto_chunk(retval, sizeof(sat), &sat);
+	sctp_addto_chunk(retval, num_types * sizeof(__u16), &types);
+
+	sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param);
+nodata:
+	if (addrs.v)
+		kfree(addrs.v);
+	return retval;
+}
+
+sctp_chunk_t *sctp_make_init_ack(const struct sctp_association *asoc,
+				 const sctp_chunk_t *chunk,
+				 int gfp, int unkparam_len)
+{
+	sctp_inithdr_t initack;
+	sctp_chunk_t *retval;
+	union sctp_params addrs;
+	int addrs_len;
+	sctp_cookie_param_t *cookie;
+	int cookie_len;
+	size_t chunksize;
+
+	retval = NULL;
+
+	/* Note: there may be no addresses to embed. */
+	addrs = sctp_bind_addrs_to_raw(&asoc->base.bind_addr, &addrs_len, gfp);
+
+	initack.init_tag	        = htonl(asoc->c.my_vtag);
+	initack.a_rwnd			= htonl(asoc->rwnd);
+	initack.num_outbound_streams	= htons(asoc->c.sinit_num_ostreams);
+	initack.num_inbound_streams	= htons(asoc->c.sinit_max_instreams);
+	initack.initial_tsn		= htonl(asoc->c.initial_tsn);
+
+	/* FIXME:  We really ought to build the cookie right
+	 * into the packet instead of allocating more fresh memory.
+	 */
+	cookie = sctp_pack_cookie(asoc->ep, asoc, chunk, &cookie_len,
+				  addrs.v, addrs_len);
+	if (!cookie)
+		goto nomem_cookie;
+
+	/* Calculate the total size of allocation, include the reserved
+	 * space for reporting unknown parameters if it is specified.
+	 */
+	chunksize = sizeof(initack) + addrs_len + cookie_len + unkparam_len;
+
+        /* Tell peer that we'll do ECN only if peer advertised such cap.  */
+	if (asoc->peer.ecn_capable)
+		chunksize += sizeof(ecap_param);
+
+	/* Now allocate and fill out the chunk.  */
+	retval = sctp_make_chunk(asoc, SCTP_CID_INIT_ACK, 0, chunksize);
+	if (!retval)
+		goto nomem_chunk;
+
+	/* Per the advice in RFC 2960 6.4, send this reply to
+	 * the source of the INIT packet.
+	 */
+	retval->transport = chunk->transport;
+	retval->subh.init_hdr =
+		sctp_addto_chunk(retval, sizeof(initack), &initack);
+	retval->param_hdr.v = sctp_addto_chunk(retval, addrs_len, addrs.v);
+	sctp_addto_chunk(retval, cookie_len, cookie);
+	if (asoc->peer.ecn_capable)
+		sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param);
+
+	/* We need to remove the const qualifier at this point.  */
+	retval->asoc = (struct sctp_association *) asoc;
+
+	/* RFC 2960 6.4 Multi-homed SCTP Endpoints
+	 *
+	 * An endpoint SHOULD transmit reply chunks (e.g., SACK,
+	 * HEARTBEAT ACK, * etc.) to the same destination transport
+	 * address from which it received the DATA or control chunk
+	 * to which it is replying.
+	 *
+	 * [INIT ACK back to where the INIT came from.]
+	 */
+	if (chunk)
+		retval->transport = chunk->transport;
+
+nomem_chunk:
+	kfree(cookie);
+nomem_cookie:
+	if (addrs.v)
+		kfree(addrs.v);
+	return retval;
+}
+
+/* 3.3.11 Cookie Echo (COOKIE ECHO) (10):
+ *
+ * This chunk is used only during the initialization of an association.
+ * It is sent by the initiator of an association to its peer to complete
+ * the initialization process. This chunk MUST precede any DATA chunk
+ * sent within the association, but MAY be bundled with one or more DATA
+ * chunks in the same packet.
+ *
+ *      0                   1                   2                   3
+ *      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *     |   Type = 10   |Chunk  Flags   |         Length                |
+ *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *     /                     Cookie                                    /
+ *     \                                                               \
+ *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Chunk Flags: 8 bit
+ *
+ *   Set to zero on transmit and ignored on receipt.
+ *
+ * Length: 16 bits (unsigned integer)
+ *
+ *   Set to the size of the chunk in bytes, including the 4 bytes of
+ *   the chunk header and the size of the Cookie.
+ *
+ * Cookie: variable size
+ *
+ *   This field must contain the exact cookie received in the
+ *   State Cookie parameter from the previous INIT ACK.
+ *
+ *   An implementation SHOULD make the cookie as small as possible
+ *   to insure interoperability.
+ */
+sctp_chunk_t *sctp_make_cookie_echo(const struct sctp_association *asoc,
+				    const sctp_chunk_t *chunk)
+{
+	sctp_chunk_t *retval;
+	void *cookie;
+	int cookie_len;
+
+	cookie = asoc->peer.cookie;
+	cookie_len = asoc->peer.cookie_len;
+
+	/* Build a cookie echo chunk.  */
+	retval = sctp_make_chunk(asoc, SCTP_CID_COOKIE_ECHO, 0, cookie_len);
+	if (!retval)
+		goto nodata;
+	retval->subh.cookie_hdr =
+		sctp_addto_chunk(retval, cookie_len, cookie);
+
+	/* RFC 2960 6.4 Multi-homed SCTP Endpoints
+	 *
+	 * An endpoint SHOULD transmit reply chunks (e.g., SACK,
+	 * HEARTBEAT ACK, * etc.) to the same destination transport
+	 * address from which it * received the DATA or control chunk
+	 * to which it is replying.
+	 *
+	 * [COOKIE ECHO back to where the INIT ACK came from.]
+	 */
+	if (chunk)
+		retval->transport = chunk->transport;
+
+nodata:
+	return retval;
+}
+
+/* 3.3.12 Cookie Acknowledgement (COOKIE ACK) (11):
+ *
+ * This chunk is used only during the initialization of an
+ * association.  It is used to acknowledge the receipt of a COOKIE
+ * ECHO chunk.  This chunk MUST precede any DATA or SACK chunk sent
+ * within the association, but MAY be bundled with one or more DATA
+ * chunks or SACK chunk in the same SCTP packet.
+ *
+ *      0                   1                   2                   3
+ *      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *     |   Type = 11   |Chunk  Flags   |     Length = 4                |
+ *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Chunk Flags: 8 bits
+ *
+ *   Set to zero on transmit and ignored on receipt.
+ */
+sctp_chunk_t *sctp_make_cookie_ack(const struct sctp_association *asoc,
+				   const sctp_chunk_t *chunk)
+{
+	sctp_chunk_t *retval;
+
+	retval = sctp_make_chunk(asoc, SCTP_CID_COOKIE_ACK, 0, 0);
+
+	/* RFC 2960 6.4 Multi-homed SCTP Endpoints
+	 *
+	 * An endpoint SHOULD transmit reply chunks (e.g., SACK,
+	 * HEARTBEAT ACK, * etc.) to the same destination transport
+	 * address from which it * received the DATA or control chunk
+	 * to which it is replying.
+	 *
+	 * [COOKIE ACK back to where the COOKIE ECHO came from.]
+	 */
+	if (retval && chunk)
+		retval->transport = chunk->transport;
+
+	return retval;
+}
+
+/*
+ *  Appendix A: Explicit Congestion Notification:
+ *  CWR:
+ *
+ *  RFC 2481 details a specific bit for a sender to send in the header of
+ *  its next outbound TCP segment to indicate to its peer that it has
+ *  reduced its congestion window.  This is termed the CWR bit.  For
+ *  SCTP the same indication is made by including the CWR chunk.
+ *  This chunk contains one data element, i.e. the TSN number that
+ *  was sent in the ECNE chunk.  This element represents the lowest
+ *  TSN number in the datagram that was originally marked with the
+ *  CE bit.
+ *
+ *     0                   1                   2                   3
+ *     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *    | Chunk Type=13 | Flags=00000000|    Chunk Length = 8           |
+ *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *    |                      Lowest TSN Number                        |
+ *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *     Note: The CWR is considered a Control chunk.
+ */
+sctp_chunk_t *sctp_make_cwr(const struct sctp_association *asoc,
+			    const __u32 lowest_tsn,
+			    const sctp_chunk_t *chunk)
+{
+	sctp_chunk_t *retval;
+	sctp_cwrhdr_t cwr;
+
+	cwr.lowest_tsn = htonl(lowest_tsn);
+	retval = sctp_make_chunk(asoc, SCTP_CID_ECN_CWR, 0,
+				 sizeof(sctp_cwrhdr_t));
+
+	if (!retval)
+		goto nodata;
+
+	retval->subh.ecn_cwr_hdr =
+		sctp_addto_chunk(retval, sizeof(cwr), &cwr);
+
+	/* RFC 2960 6.4 Multi-homed SCTP Endpoints
+	 *
+	 * An endpoint SHOULD transmit reply chunks (e.g., SACK,
+	 * HEARTBEAT ACK, * etc.) to the same destination transport
+	 * address from which it * received the DATA or control chunk
+	 * to which it is replying.
+	 *
+	 * [Report a reduced congestion window back to where the ECNE
+	 * came from.]
+	 */
+	if (chunk)
+		retval->transport = chunk->transport;
+
+nodata:
+	return retval;
+}
+
+/* Make an ECNE chunk.  This is a congestion experienced report.  */
+sctp_chunk_t *sctp_make_ecne(const struct sctp_association *asoc,
+			     const __u32 lowest_tsn)
+{
+	sctp_chunk_t *retval;
+	sctp_ecnehdr_t ecne;
+
+	ecne.lowest_tsn = htonl(lowest_tsn);
+	retval = sctp_make_chunk(asoc, SCTP_CID_ECN_ECNE, 0,
+				 sizeof(sctp_ecnehdr_t));
+	if (!retval)
+		goto nodata;
+	retval->subh.ecne_hdr =
+		sctp_addto_chunk(retval, sizeof(ecne), &ecne);
+
+nodata:
+	return retval;
+}
+
+/* Make a DATA chunk for the given association from the provided
+ * parameters.  However, do not populate the data payload.
+ */
+sctp_chunk_t *sctp_make_datafrag_empty(struct sctp_association *asoc,
+				       const struct sctp_sndrcvinfo *sinfo,
+				       int data_len, __u8 flags, __u16 ssn)
+{
+	sctp_chunk_t *retval;
+	sctp_datahdr_t dp;
+	int chunk_len;
+
+	/* We assign the TSN as LATE as possible, not here when
+	 * creating the chunk.
+	 */
+	dp.tsn= 1000000;       /* This marker is a debugging aid. */
+	dp.stream = htons(sinfo->sinfo_stream);
+	dp.ppid   = htonl(sinfo->sinfo_ppid);
+	dp.ssn    = htons(ssn);
+
+	/* Set the flags for an unordered send.  */
+	if (sinfo->sinfo_flags & MSG_UNORDERED)
+		flags |= SCTP_DATA_UNORDERED;
+
+	chunk_len = sizeof(dp) + data_len;
+	retval = sctp_make_chunk(asoc, SCTP_CID_DATA, flags, chunk_len);
+	if (!retval)
+		goto nodata;
+
+	retval->subh.data_hdr = sctp_addto_chunk(retval, sizeof(dp), &dp);
+	memcpy(&retval->sinfo, sinfo, sizeof(struct sctp_sndrcvinfo));
+
+nodata:
+	return retval;
+}
+
+/* Make a DATA chunk for the given association.  Populate the data
+ * payload.
+ */
+sctp_chunk_t *sctp_make_datafrag(struct sctp_association *asoc,
+				 const struct sctp_sndrcvinfo *sinfo,
+				 int data_len, const __u8 *data,
+				 __u8 flags, __u16 ssn)
+{
+	sctp_chunk_t *retval;
+
+	retval = sctp_make_datafrag_empty(asoc, sinfo, data_len, flags, ssn);
+	if (retval)
+		sctp_addto_chunk(retval, data_len, data);
+
+	return retval;
+}
+
+/* Make a DATA chunk for the given association to ride on stream id
+ * 'stream', with a payload id of 'payload', and a body of 'data'.
+ */
+sctp_chunk_t *sctp_make_data(struct sctp_association *asoc,
+			     const struct sctp_sndrcvinfo *sinfo,
+			     int data_len, const __u8 *data)
+{
+	sctp_chunk_t *retval = NULL;
+
+	retval = sctp_make_data_empty(asoc, sinfo, data_len);
+	if (retval)
+		sctp_addto_chunk(retval, data_len, data);
+        return retval;
+}
+
+/* Make a DATA chunk for the given association to ride on stream id
+ * 'stream', with a payload id of 'payload', and a body big enough to
+ * hold 'data_len' octets of data.  We use this version when we need
+ * to build the message AFTER allocating memory.
+ */
+sctp_chunk_t *sctp_make_data_empty(struct sctp_association *asoc,
+				   const struct sctp_sndrcvinfo *sinfo,
+				   int data_len)
+{
+	__u8 flags = SCTP_DATA_NOT_FRAG;
+
+	return sctp_make_datafrag_empty(asoc, sinfo, data_len, flags, 0);
+}
+
+/* Create a selective ackowledgement (SACK) for the given
+ * association.  This reports on which TSN's we've seen to date,
+ * including duplicates and gaps.
+ */
+sctp_chunk_t *sctp_make_sack(const struct sctp_association *asoc)
+{
+	sctp_chunk_t *retval;
+	sctp_sackhdr_t sack;
+	sctp_gap_ack_block_t gab;
+	int length;
+	__u32 ctsn;
+	struct sctp_tsnmap_iter iter;
+	__u16 num_gabs, num_dup_tsns;
+	struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map;
+
+	ctsn = sctp_tsnmap_get_ctsn(map);
+	SCTP_DEBUG_PRINTK("sackCTSNAck sent is 0x%x.\n", ctsn);
+
+	/* Count the number of Gap Ack Blocks.  */
+	num_gabs = 0;
+
+	if (sctp_tsnmap_has_gap(map)) {
+		sctp_tsnmap_iter_init(map, &iter);
+		while (sctp_tsnmap_next_gap_ack(map, &iter,
+						&gab.start, &gab.end))
+			num_gabs++;
+	}
+
+	num_dup_tsns = sctp_tsnmap_num_dups(map);
+
+	/* Initialize the SACK header.  */
+	sack.cum_tsn_ack	    = htonl(ctsn);
+	sack.a_rwnd 		    = htonl(asoc->a_rwnd);
+	sack.num_gap_ack_blocks     = htons(num_gabs);
+	sack.num_dup_tsns           = htons(num_dup_tsns);
+
+	length = sizeof(sack)
+		+ sizeof(sctp_gap_ack_block_t) * num_gabs
+		+ sizeof(__u32) * num_dup_tsns;
+
+	/* Create the chunk.  */
+	retval = sctp_make_chunk(asoc, SCTP_CID_SACK, 0, length);
+	if (!retval)
+		goto nodata;
+
+	/* RFC 2960 6.4 Multi-homed SCTP Endpoints
+	 *
+	 * An endpoint SHOULD transmit reply chunks (e.g., SACK,
+	 * HEARTBEAT ACK, etc.) to the same destination transport
+	 * address from which it received the DATA or control chunk to
+	 * which it is replying.  This rule should also be followed if
+	 * the endpoint is bundling DATA chunks together with the
+	 * reply chunk.
+	 *
+	 * However, when acknowledging multiple DATA chunks received
+	 * in packets from different source addresses in a single
+	 * SACK, the SACK chunk may be transmitted to one of the
+	 * destination transport addresses from which the DATA or
+	 * control chunks being acknowledged were received.
+	 *
+	 * [BUG:  We do not implement the following paragraph.
+	 * Perhaps we should remember the last transport we used for a
+	 * SACK and avoid that (if possible) if we have seen any
+	 * duplicates. --piggy]
+	 *
+	 * When a receiver of a duplicate DATA chunk sends a SACK to a
+	 * multi- homed endpoint it MAY be beneficial to vary the
+	 * destination address and not use the source address of the
+	 * DATA chunk.  The reason being that receiving a duplicate
+	 * from a multi-homed endpoint might indicate that the return
+	 * path (as specified in the source address of the DATA chunk)
+	 * for the SACK is broken.
+	 *
+	 * [Send to the address from which we last received a DATA chunk.]
+	 */
+	retval->transport = asoc->peer.last_data_from;
+
+	retval->subh.sack_hdr =
+		sctp_addto_chunk(retval, sizeof(sack), &sack);
+
+	/* Put the Gap Ack Blocks into the chunk.  */
+	if (num_gabs) {
+		sctp_tsnmap_iter_init(map, &iter);
+		while(sctp_tsnmap_next_gap_ack(map, &iter,
+					       &gab.start, &gab.end)) {
+			gab.start = htons(gab.start);
+			gab.end = htons(gab.end);
+			sctp_addto_chunk(retval, sizeof(sctp_gap_ack_block_t),
+					 &gab);
+		}
+	}
+
+	/* Register the duplicates.  */
+	sctp_addto_chunk(retval, sizeof(__u32) * num_dup_tsns,
+			 sctp_tsnmap_get_dups(map));
+
+nodata:
+	return retval;
+}
+
+/* Make a SHUTDOWN chunk. */
+sctp_chunk_t *sctp_make_shutdown(const struct sctp_association *asoc)
+{
+	sctp_chunk_t *retval;
+	sctp_shutdownhdr_t shut;
+	__u32 ctsn;
+
+	ctsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map);
+	shut.cum_tsn_ack = htonl(ctsn);
+
+	retval = sctp_make_chunk(asoc, SCTP_CID_SHUTDOWN, 0,
+				 sizeof(sctp_shutdownhdr_t));
+	if (!retval)
+		goto nodata;
+
+	retval->subh.shutdown_hdr =
+		sctp_addto_chunk(retval, sizeof(shut), &shut);
+nodata:
+	return retval;
+}
+
+sctp_chunk_t *sctp_make_shutdown_ack(const struct sctp_association *asoc,
+				     const sctp_chunk_t *chunk)
+{
+	sctp_chunk_t *retval;
+
+	retval = sctp_make_chunk(asoc, SCTP_CID_SHUTDOWN_ACK, 0, 0);
+
+	/* RFC 2960 6.4 Multi-homed SCTP Endpoints
+	 *
+	 * An endpoint SHOULD transmit reply chunks (e.g., SACK,
+	 * HEARTBEAT ACK, * etc.) to the same destination transport
+	 * address from which it * received the DATA or control chunk
+	 * to which it is replying.
+	 *
+	 * [ACK back to where the SHUTDOWN came from.]
+	 */
+	if (retval && chunk)
+		retval->transport = chunk->transport;
+
+	return retval;
+}
+
+sctp_chunk_t *sctp_make_shutdown_complete(const struct sctp_association *asoc,
+					  const sctp_chunk_t *chunk)
+{
+	sctp_chunk_t *retval;
+	__u8 flags = 0;
+
+	/* Maybe set the T-bit if we have no association. */
+	flags |= asoc ? 0 : SCTP_CHUNK_FLAG_T;
+
+	retval = sctp_make_chunk(asoc, SCTP_CID_SHUTDOWN_COMPLETE, flags, 0);
+
+	/* RFC 2960 6.4 Multi-homed SCTP Endpoints
+	 *
+	 * An endpoint SHOULD transmit reply chunks (e.g., SACK,
+	 * HEARTBEAT ACK, * etc.) to the same destination transport
+	 * address from which it * received the DATA or control chunk
+	 * to which it is replying.
+	 *
+	 * [Report SHUTDOWN COMPLETE back to where the SHUTDOWN ACK
+	 * came from.]
+	 */
+	if (retval && chunk)
+		retval->transport = chunk->transport;
+
+        return retval;
+}
+
+/* Create an ABORT.  Note that we set the T bit if we have no
+ * association.
+ */
+sctp_chunk_t *sctp_make_abort(const struct sctp_association *asoc,
+			      const sctp_chunk_t *chunk,
+			      const size_t hint)
+{
+	sctp_chunk_t *retval;
+	__u8 flags = 0;
+
+	/* Maybe set the T-bit if we have no association.  */
+	flags |= asoc ? 0 : SCTP_CHUNK_FLAG_T;
+
+	retval = sctp_make_chunk(asoc, SCTP_CID_ABORT, flags, hint);
+
+	/* RFC 2960 6.4 Multi-homed SCTP Endpoints
+	 *
+	 * An endpoint SHOULD transmit reply chunks (e.g., SACK,
+	 * HEARTBEAT ACK, * etc.) to the same destination transport
+	 * address from which it * received the DATA or control chunk
+	 * to which it is replying.
+	 *
+	 * [ABORT back to where the offender came from.]
+	 */
+	if (retval && chunk)
+		retval->transport = chunk->transport;
+
+	return retval;
+}
+
+/* Helper to create ABORT with a NO_USER_DATA error.  */
+sctp_chunk_t *sctp_make_abort_no_data(const struct sctp_association *asoc,
+				      const sctp_chunk_t *chunk, __u32 tsn)
+{
+	sctp_chunk_t *retval;
+	__u32 payload;
+
+	retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t)
+				 + sizeof(tsn));
+
+	if (!retval)
+		goto no_mem;
+
+	/* Put the tsn back into network byte order.  */
+	payload = htonl(tsn);
+	sctp_init_cause(retval, SCTP_ERROR_NO_DATA, (const void *)&payload,
+			sizeof(payload));
+
+	/* RFC 2960 6.4 Multi-homed SCTP Endpoints
+	 *
+	 * An endpoint SHOULD transmit reply chunks (e.g., SACK,
+	 * HEARTBEAT ACK, * etc.) to the same destination transport
+	 * address from which it * received the DATA or control chunk
+	 * to which it is replying.
+	 *
+	 * [ABORT back to where the offender came from.]
+	 */
+	if (chunk)
+		retval->transport = chunk->transport;
+
+no_mem:
+	return retval;
+}
+
+/* Helper to create ABORT with a SCTP_ERROR_USER_ABORT error.  */
+sctp_chunk_t *sctp_make_abort_user(const struct sctp_association *asoc,
+				   const sctp_chunk_t *chunk,
+				   const struct msghdr *msg)
+{
+	sctp_chunk_t *retval;
+	void *payload = NULL, *payoff;
+	size_t paylen;
+	struct iovec *iov = msg->msg_iov;
+	int iovlen = msg->msg_iovlen;
+
+	paylen = get_user_iov_size(iov, iovlen);
+	retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + paylen);
+	if (!retval)
+		goto err_chunk;
+
+	if (paylen) {
+		/* Put the msg_iov together into payload.  */
+		payload = kmalloc(paylen, GFP_ATOMIC);
+		if (!payload)
+			goto err_payload;
+		payoff = payload;
+
+		for (; iovlen > 0; --iovlen) {
+			if (copy_from_user(payoff, iov->iov_base,iov->iov_len))
+				goto err_copy;
+			payoff += iov->iov_len;
+			iov++;
+		}
+	}
+
+	sctp_init_cause(retval, SCTP_ERROR_USER_ABORT, payload, paylen);
+
+	if (paylen)
+		kfree(payload);
+
+	return retval;
+
+err_copy:
+	kfree(payload);
+err_payload:
+	sctp_free_chunk(retval);
+	retval = NULL;
+err_chunk:
+	return retval;
+}
+
+/* Make a HEARTBEAT chunk.  */
+sctp_chunk_t *sctp_make_heartbeat(const struct sctp_association *asoc,
+				  const struct sctp_transport *transport,
+				  const void *payload, const size_t paylen)
+{
+	sctp_chunk_t *retval = sctp_make_chunk(asoc, SCTP_CID_HEARTBEAT,
+					       0, paylen);
+
+	if (!retval)
+		goto nodata;
+
+	/* Cast away the 'const', as this is just telling the chunk
+	 * what transport it belongs to.
+	 */
+	retval->transport = (struct sctp_transport *) transport;
+	retval->subh.hbs_hdr = sctp_addto_chunk(retval, paylen, payload);
+
+nodata:
+	return retval;
+}
+
+sctp_chunk_t *sctp_make_heartbeat_ack(const struct sctp_association *asoc,
+				      const sctp_chunk_t *chunk,
+				      const void *payload, const size_t paylen)
+{
+	sctp_chunk_t *retval = sctp_make_chunk(asoc, SCTP_CID_HEARTBEAT_ACK,
+					       0, paylen);
+
+	if (!retval)
+		goto nodata;
+	retval->subh.hbs_hdr = sctp_addto_chunk(retval, paylen, payload);
+
+	/* RFC 2960 6.4 Multi-homed SCTP Endpoints
+	 *
+	 * An endpoint SHOULD transmit reply chunks (e.g., SACK,
+	 * HEARTBEAT ACK, * etc.) to the same destination transport
+	 * address from which it * received the DATA or control chunk
+	 * to which it is replying.
+	 *
+	 * [HBACK back to where the HEARTBEAT came from.]
+	 */
+	if (chunk)
+		retval->transport = chunk->transport;
+
+nodata:
+	return retval;
+}
+
+/* Create an Operation Error chunk with the specified space reserved.
+ * This routine can be used for containing multiple causes in the chunk.
+ */
+sctp_chunk_t *sctp_make_op_error_space(const struct sctp_association *asoc,
+				       const sctp_chunk_t *chunk,
+				       size_t size)
+{
+	sctp_chunk_t *retval;
+
+	retval = sctp_make_chunk(asoc, SCTP_CID_ERROR, 0,
+				 sizeof(sctp_errhdr_t) + size);
+	if (!retval)
+		goto nodata;
+
+	/* RFC 2960 6.4 Multi-homed SCTP Endpoints
+	 *
+	 * An endpoint SHOULD transmit reply chunks (e.g., SACK,
+	 * HEARTBEAT ACK, etc.) to the same destination transport
+	 * address from which it received the DATA or control chunk
+	 * to which it is replying.
+	 *
+	 */
+	if (chunk)
+		retval->transport = chunk->transport;
+
+nodata:
+	return retval;
+}
+
+/* Create an Operation Error chunk.  */
+sctp_chunk_t *sctp_make_op_error(const struct sctp_association *asoc,
+				 const sctp_chunk_t *chunk,
+				 __u16 cause_code, const void *payload,
+				 size_t paylen)
+{
+	sctp_chunk_t *retval = sctp_make_op_error_space(asoc, chunk, paylen);
+
+	if (!retval)
+		goto nodata;
+
+	sctp_init_cause(retval, cause_code, payload, paylen);
+
+nodata:
+	return retval;
+}
+
+/********************************************************************
+ * 2nd Level Abstractions
+ ********************************************************************/
+
+/* Turn an skb into a chunk.
+ * FIXME: Eventually move the structure directly inside the skb->cb[].
+ */
+sctp_chunk_t *sctp_chunkify(struct sk_buff *skb, 
+			    const struct sctp_association *asoc,
+			    struct sock *sk)
+{
+	sctp_chunk_t *retval = t_new(sctp_chunk_t, GFP_ATOMIC);
+
+	if (!retval)
+		goto nodata;
+	memset(retval, 0, sizeof(sctp_chunk_t));
+
+	if (!sk) {
+		SCTP_DEBUG_PRINTK("chunkifying skb %p w/o an sk\n", skb);
+	}
+
+	retval->skb		= skb;
+	retval->asoc		= (struct sctp_association *)asoc;
+	retval->num_times_sent	= 0;
+	retval->has_tsn		= 0;
+	retval->has_ssn         = 0;
+	retval->rtt_in_progress	= 0;
+	retval->sent_at	= jiffies;
+	retval->singleton	= 1;
+	retval->end_of_packet	= 0;
+	retval->ecn_ce_done	= 0;
+	retval->pdiscard	= 0;
+
+	/* sctpimpguide-05.txt Section 2.8.2
+	 * M1) Each time a new DATA chunk is transmitted
+	 * set the 'TSN.Missing.Report' count for that TSN to 0. The
+	 * 'TSN.Missing.Report' count will be used to determine missing chunks
+	 * and when to fast retransmit.
+	 */
+	retval->tsn_missing_report = 0;
+	retval->tsn_gap_acked = 0;
+	retval->fast_retransmit = 0;
+
+	/* Polish the bead hole.  */
+	INIT_LIST_HEAD(&retval->transmitted_list);
+	INIT_LIST_HEAD(&retval->frag_list);
+	SCTP_DBG_OBJCNT_INC(chunk);
+
+nodata:
+	return retval;
+}
+
+/* Set chunk->source and dest based on the IP header in chunk->skb.  */
+void sctp_init_addrs(sctp_chunk_t *chunk, union sctp_addr *src,
+		     union sctp_addr *dest)
+{
+	memcpy(&chunk->source, src, sizeof(union sctp_addr));
+	memcpy(&chunk->dest, dest, sizeof(union sctp_addr));
+}
+
+/* Extract the source address from a chunk.  */
+const union sctp_addr *sctp_source(const sctp_chunk_t *chunk)
+{
+	/* If we have a known transport, use that.  */
+	if (chunk->transport) {
+		return &chunk->transport->ipaddr;
+	} else {
+		/* Otherwise, extract it from the IP header.  */
+		return &chunk->source;
+	}
+}
+
+/* Create a new chunk, setting the type and flags headers from the
+ * arguments, reserving enough space for a 'paylen' byte payload.
+ */
+sctp_chunk_t *sctp_make_chunk(const struct sctp_association *asoc,
+			      __u8 type, __u8 flags, int paylen)
+{
+	sctp_chunk_t *retval;
+	sctp_chunkhdr_t *chunk_hdr;
+	struct sk_buff *skb;
+	struct sock *sk;
+
+	/* No need to allocate LL here, as this is only a chunk. */
+	skb = alloc_skb(WORD_ROUND(sizeof(sctp_chunkhdr_t) + paylen),
+			GFP_ATOMIC);
+	if (!skb)
+		goto nodata;
+
+	/* Make room for the chunk header.  */
+	chunk_hdr = (sctp_chunkhdr_t *)skb_put(skb, sizeof(sctp_chunkhdr_t));
+	chunk_hdr->type	  = type;
+	chunk_hdr->flags  = flags;
+	chunk_hdr->length = htons(sizeof(sctp_chunkhdr_t));
+
+	sk = asoc ? asoc->base.sk : NULL;
+	retval = sctp_chunkify(skb, asoc, sk);
+	if (!retval) {
+		kfree_skb(skb);
+		goto nodata;
+	}
+
+	retval->chunk_hdr = chunk_hdr;
+	retval->chunk_end = ((__u8 *)chunk_hdr) + sizeof(sctp_chunkhdr_t);
+
+	/* Set the skb to the belonging sock for accounting.  */
+	skb->sk = sk;
+
+	return retval;
+
+nodata:
+	return NULL;
+}
+
+/* Release the memory occupied by a chunk.  */
+void sctp_free_chunk(sctp_chunk_t *chunk)
+{
+	/* Make sure that we are not on any list.  */
+	skb_unlink((struct sk_buff *) chunk);
+	list_del(&chunk->transmitted_list);
+
+	/* Free the chunk skb data and the SCTP_chunk stub itself. */
+	dev_kfree_skb(chunk->skb);
+
+	kfree(chunk);
+	SCTP_DBG_OBJCNT_DEC(chunk);
+}
+
+
+/* Append bytes to the end of a chunk.  Will panic if chunk is not big
+ * enough.
+ */
+void *sctp_addto_chunk(sctp_chunk_t *chunk, int len, const void *data)
+{
+	void *target;
+	void *padding;
+	int chunklen = ntohs(chunk->chunk_hdr->length);
+	int padlen = chunklen % 4;
+
+	padding = skb_put(chunk->skb, padlen);
+	target = skb_put(chunk->skb, len);
+
+	memset(padding, 0, padlen);
+	memcpy(target, data, len);
+
+	/* Adjust the chunk length field.  */
+	chunk->chunk_hdr->length = htons(chunklen + padlen + len);
+	chunk->chunk_end = chunk->skb->tail;
+
+	return target;
+}
+
+/* Append bytes from user space to the end of a chunk.  Will panic if
+ * chunk is not big enough.
+ * Returns a kernel err value.
+ */
+static int sctp_user_addto_chunk(sctp_chunk_t *chunk, int off, int len,
+				 struct iovec *data)
+{
+	__u8 *target;
+	int err = 0;
+
+	/* Make room in chunk for data.  */
+	target = skb_put(chunk->skb, len);
+
+	/* Copy data (whole iovec) into chunk */
+	if ((err = memcpy_fromiovecend(target, data, off, len)))
+		goto out;
+
+	/* Adjust the chunk length field.  */
+	chunk->chunk_hdr->length =
+		htons(ntohs(chunk->chunk_hdr->length) + len);
+	chunk->chunk_end = chunk->skb->tail;
+
+out:
+	return err;
+}
+
+/* A data chunk can have a maximum payload of (2^16 - 20).  Break
+ * down any such message into smaller chunks.  Opportunistically, fragment
+ * the chunks down to the current MTU constraints.  We may get refragmented
+ * later if the PMTU changes, but it is _much better_ to fragment immediately
+ * with a reasonable guess than always doing our fragmentation on the
+ * soft-interrupt.
+ */
+
+
+int sctp_datachunks_from_user(struct sctp_association *asoc,
+			      const struct sctp_sndrcvinfo *sinfo,
+			      struct msghdr *msg, int msg_len,
+			      struct sk_buff_head *chunks)
+{
+	int max, whole, i, offset, over, err;
+	int len, first_len;
+	sctp_chunk_t *chunk;
+	__u8 frag;
+
+	/* What is a reasonable fragmentation point right now? */
+	max = asoc->pmtu;
+	if (max < SCTP_MIN_PMTU)
+		max = SCTP_MIN_PMTU;
+	max -= SCTP_IP_OVERHEAD;
+
+	/* Make sure not beyond maximum chunk size. */
+	if (max > SCTP_MAX_CHUNK_LEN)
+		max = SCTP_MAX_CHUNK_LEN;
+
+	/* Subtract out the overhead of a data chunk header. */
+	max -= sizeof(struct sctp_data_chunk);
+
+	whole = 0;
+	first_len = max;
+
+	/* Encourage Cookie-ECHO bundling. */
+	if (asoc->state < SCTP_STATE_COOKIE_ECHOED) {
+		whole = msg_len / (max - SCTP_ARBITRARY_COOKIE_ECHO_LEN);
+
+		/* Account for the DATA to be bundled with the COOKIE-ECHO. */
+		if (whole) {
+			first_len = max - SCTP_ARBITRARY_COOKIE_ECHO_LEN;
+			msg_len -= first_len;
+			whole = 1;
+		}
+	}
+
+	/* How many full sized?  How many bytes leftover? */
+	whole += msg_len / max;
+	over = msg_len % max;
+	offset = 0;
+
+	if (whole && over)
+		SCTP_INC_STATS_USER(SctpFragUsrMsgs);
+
+	/* Create chunks for all the full sized DATA chunks. */
+	for (i=0, len=first_len; i < whole; i++) {
+		frag = SCTP_DATA_MIDDLE_FRAG;
+
+		if (0 == i)
+			frag |= SCTP_DATA_FIRST_FRAG;
+
+		if ((i == (whole - 1)) && !over)
+			frag |= SCTP_DATA_LAST_FRAG;
+
+		chunk = sctp_make_datafrag_empty(asoc, sinfo, len, frag, 0);
+
+		if (!chunk)
+			goto nomem;
+		err = sctp_user_addto_chunk(chunk, offset, len, msg->msg_iov);
+		if (err < 0)
+			goto errout;
+
+		offset += len;
+
+		/* Put the chunk->skb back into the form expected by send.  */
+		__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr
+			   - (__u8 *)chunk->skb->data);
+
+		__skb_queue_tail(chunks, (struct sk_buff *)chunk);
+
+		/* The first chunk, the first chunk was likely short
+		 * to allow bundling, so reset to full size.
+		 */
+		if (0 == i)
+			len = max;
+	}
+
+	/* .. now the leftover bytes. */
+	if (over) {
+		if (!whole)
+			frag = SCTP_DATA_NOT_FRAG;
+		else
+			frag = SCTP_DATA_LAST_FRAG;
+
+		chunk = sctp_make_datafrag_empty(asoc, sinfo, over, frag, 0);
+
+		if (!chunk)
+			goto nomem;
+
+		err = sctp_user_addto_chunk(chunk, offset, over, msg->msg_iov);
+
+		/* Put the chunk->skb back into the form expected by send.  */
+		__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr
+			   - (__u8 *)chunk->skb->data);
+		if (err < 0)
+			goto errout;
+
+		__skb_queue_tail(chunks, (struct sk_buff *)chunk);
+	}
+	err = 0;
+	goto out;
+
+nomem:
+	err = -ENOMEM;
+errout:
+	while ((chunk = (sctp_chunk_t *)__skb_dequeue(chunks)))
+		sctp_free_chunk(chunk);
+out:
+	return err;
+}
+
+/* Helper function to assign a TSN if needed.  This assumes that both
+ * the data_hdr and association have already been assigned.
+ */
+void sctp_chunk_assign_ssn(sctp_chunk_t *chunk)
+{
+	__u16 ssn;
+	__u16 sid;
+
+	if (chunk->has_ssn)
+		return;
+
+	/* This is the last possible instant to assign a SSN. */
+	if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) {
+		ssn = 0;
+	} else {
+		sid = htons(chunk->subh.data_hdr->stream);
+		if (chunk->chunk_hdr->flags & SCTP_DATA_LAST_FRAG)
+			ssn = sctp_ssn_next(&chunk->asoc->ssnmap->out, sid);
+		else
+			ssn = sctp_ssn_peek(&chunk->asoc->ssnmap->out, sid);
+		ssn = htons(ssn);
+	}
+
+	chunk->subh.data_hdr->ssn = ssn;
+	chunk->has_ssn = 1;
+}
+
+/* Helper function to assign a TSN if needed.  This assumes that both
+ * the data_hdr and association have already been assigned.
+ */
+void sctp_chunk_assign_tsn(sctp_chunk_t *chunk)
+{
+	if (!chunk->has_tsn) {
+		/* This is the last possible instant to
+		 * assign a TSN.
+		 */
+		chunk->subh.data_hdr->tsn =
+			htonl(sctp_association_get_next_tsn(chunk->asoc));
+		chunk->has_tsn = 1;
+	}
+}
+
+/* Create a CLOSED association to use with an incoming packet.  */
+struct sctp_association *sctp_make_temp_asoc(const struct sctp_endpoint *ep,
+					struct sctp_chunk *chunk, int gfp)
+{
+	struct sctp_association *asoc;
+	struct sk_buff *skb;
+	sctp_scope_t scope;
+
+	/* Create the bare association.  */
+	scope = sctp_scope(sctp_source(chunk));
+	asoc = sctp_association_new(ep, ep->base.sk, scope, gfp);
+	if (!asoc)
+		goto nodata;
+	asoc->temp = 1;
+	skb = chunk->skb;
+	/* Create an entry for the source address of the packet.  */
+	/* FIXME: Use the af specific helpers. */
+	switch (skb->nh.iph->version) {
+	case 4:
+		asoc->c.peer_addr.v4.sin_family     = AF_INET;
+		asoc->c.peer_addr.v4.sin_port = ntohs(chunk->sctp_hdr->source);
+		asoc->c.peer_addr.v4.sin_addr.s_addr = skb->nh.iph->saddr;
+                break;
+
+	case 6:
+		asoc->c.peer_addr.v6.sin6_family     = AF_INET6;
+		asoc->c.peer_addr.v6.sin6_port
+			= ntohs(chunk->sctp_hdr->source);
+		asoc->c.peer_addr.v6.sin6_flowinfo = 0; /* BUG BUG BUG */
+		asoc->c.peer_addr.v6.sin6_addr = skb->nh.ipv6h->saddr;
+		asoc->c.peer_addr.v6.sin6_scope_id =
+			((struct inet6_skb_parm *)skb->cb)->iif;
+		break;
+
+        default:
+		/* Yikes!  I never heard of this kind of address.  */
+		goto fail;
+	};
+
+nodata:
+	return asoc;
+
+fail:
+	sctp_association_free(asoc);
+	return NULL;
+}
+
+/* Build a cookie representing asoc.
+ * This INCLUDES the param header needed to put the cookie in the INIT ACK.
+ */
+sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep,
+				      const struct sctp_association *asoc,
+				      const sctp_chunk_t *init_chunk,
+				      int *cookie_len,
+				      const __u8 *raw_addrs, int addrs_len)
+{
+	sctp_cookie_param_t *retval;
+	sctp_signed_cookie_t *cookie;
+	struct scatterlist sg;
+	int headersize, bodysize;
+	unsigned int keylen;
+	char *key;
+
+	headersize = sizeof(sctp_paramhdr_t) + SCTP_SECRET_SIZE;
+	bodysize = sizeof(sctp_cookie_t)
+		+ ntohs(init_chunk->chunk_hdr->length) + addrs_len;
+
+	/* Pad out the cookie to a multiple to make the signature
+	 * functions simpler to write.
+	 */
+	if (bodysize % SCTP_COOKIE_MULTIPLE)
+		bodysize += SCTP_COOKIE_MULTIPLE
+			- (bodysize % SCTP_COOKIE_MULTIPLE);
+	*cookie_len = headersize + bodysize;
+
+	retval = (sctp_cookie_param_t *)kmalloc(*cookie_len, GFP_ATOMIC);
+
+	if (!retval) {
+		*cookie_len = 0;
+		goto nodata;
+	}
+
+	/* Clear this memory since we are sending this data structure
+	 * out on the network.
+	 */
+	memset(retval, 0x00, *cookie_len);
+	cookie = (sctp_signed_cookie_t *) retval->body;
+
+	/* Set up the parameter header.  */
+	retval->p.type = SCTP_PARAM_STATE_COOKIE;
+	retval->p.length = htons(*cookie_len);
+
+	/* Copy the cookie part of the association itself.  */
+	cookie->c = asoc->c;
+
+	/* Save the raw address list length in the cookie. */
+	cookie->c.raw_addr_list_len = addrs_len;
+
+	/* Set an expiration time for the cookie.  */
+	do_gettimeofday(&cookie->c.expiration);
+	tv_add(&asoc->cookie_life, &cookie->c.expiration);
+
+	/* Copy the peer's init packet.  */
+	memcpy(&cookie->c.peer_init[0], init_chunk->chunk_hdr,
+	       ntohs(init_chunk->chunk_hdr->length));
+
+	/* Copy the raw local address list of the association. */
+	memcpy((__u8 *)&cookie->c.peer_init[0] + 
+	       ntohs(init_chunk->chunk_hdr->length), raw_addrs, addrs_len);
+	
+  	if (sctp_sk(ep->base.sk)->hmac) {
+		/* Sign the message.  */
+		sg.page = virt_to_page(&cookie->c);
+		sg.offset = (unsigned long)(&cookie->c) % PAGE_SIZE;
+		sg.length = bodysize;
+		keylen = SCTP_SECRET_SIZE;
+		key = (char *)ep->secret_key[ep->current_key];
+		
+		sctp_crypto_hmac(sctp_sk(ep->base.sk)->hmac, key, &keylen, 
+				 &sg, 1, cookie->signature);
+	} else
+		sctp_hash_digest(ep->secret_key[ep->current_key], 
+				 SCTP_SECRET_SIZE, (__u8 *) &cookie->c,
+				 bodysize, cookie->signature);
+
+nodata:
+	return retval;
+}
+
+/* Unpack the cookie from COOKIE ECHO chunk, recreating the association.  */
+struct sctp_association *sctp_unpack_cookie(
+	const struct sctp_endpoint *ep,
+	const struct sctp_association *asoc,
+	sctp_chunk_t *chunk, int gfp,
+	int *error, sctp_chunk_t **errp)
+{
+	struct sctp_association *retval = NULL;
+	sctp_signed_cookie_t *cookie;
+	sctp_cookie_t *bear_cookie;
+	int headersize, bodysize, fixed_size;
+	__u8 digest[SCTP_SIGNATURE_SIZE];
+	int secret;
+	struct scatterlist sg;
+	unsigned int keylen;
+	char *key;
+	sctp_scope_t scope;
+	struct sk_buff *skb = chunk->skb;
+
+	headersize = sizeof(sctp_chunkhdr_t) + SCTP_SECRET_SIZE;
+	bodysize = ntohs(chunk->chunk_hdr->length) - headersize;
+	fixed_size = headersize + sizeof(sctp_cookie_t);
+
+	/* Verify that the chunk looks like it even has a cookie.
+	 * There must be enough room for our cookie and our peer's
+	 * INIT chunk.
+	 */
+	if (ntohs(chunk->chunk_hdr->length) <
+	    (fixed_size + sizeof(sctp_chunkhdr_t)))
+		goto malformed;
+
+	/* Verify that the cookie has been padded out. */
+	if (bodysize % SCTP_COOKIE_MULTIPLE)
+		goto malformed;
+
+	/* Process the cookie.  */
+	cookie = chunk->subh.cookie_hdr;
+	bear_cookie = &cookie->c;
+
+	if (!sctp_sk(ep->base.sk)->hmac) {
+		secret = ep->current_key;
+		sctp_hash_digest(ep->secret_key[secret], SCTP_SECRET_SIZE,
+				 (__u8 *) bear_cookie, bodysize, digest);
+
+		if (memcmp(digest, cookie->signature, SCTP_SIGNATURE_SIZE)) {
+			/* Try the previous key. */
+			secret = ep->last_key;
+			sctp_hash_digest(ep->secret_key[secret], 
+					 SCTP_SECRET_SIZE, (__u8 *) bear_cookie,
+					 bodysize, digest);
+
+			if (memcmp(digest, cookie->signature, SCTP_SIGNATURE_SIZE)) {
+				/* Yikes!  Still bad signature! */
+				*error = -SCTP_IERROR_BAD_SIG;
+				goto fail;
+			}
+			
+		}
+
+		goto no_hmac;
+	}
+
+	/* Check the signature.  */
+	keylen = SCTP_SECRET_SIZE;
+	sg.page = virt_to_page(bear_cookie);
+	sg.offset = (unsigned long)(bear_cookie) % PAGE_SIZE;
+	sg.length = bodysize;
+	key = (char *)ep->secret_key[ep->current_key];
+
+	memset(digest, 0x00, sizeof(digest));
+	sctp_crypto_hmac(sctp_sk(ep->base.sk)->hmac, key, &keylen, &sg, 
+			 1, digest);
+
+	if (memcmp(digest, cookie->signature, SCTP_SIGNATURE_SIZE)) {
+		/* Try the previous key. */	
+		key = (char *)ep->secret_key[ep->last_key];
+		memset(digest, 0x00, sizeof(digest));
+		sctp_crypto_hmac(sctp_sk(ep->base.sk)->hmac, key, &keylen, 
+				 &sg, 1, digest);
+		
+		if (memcmp(digest, cookie->signature, SCTP_SIGNATURE_SIZE)) {
+			/* Yikes!  Still bad signature! */
+			*error = -SCTP_IERROR_BAD_SIG;
+			goto fail;
+		}
+	}
+
+no_hmac:
+	/* Check to see if the cookie is stale.  If there is already
+	 * an association, there is no need to check cookie's expiration
+	 * for init collision case of lost COOKIE ACK.
+	 */
+	if (!asoc && tv_lt(bear_cookie->expiration, skb->stamp)) {
+		__u16 len;
+		/*
+		 * Section 3.3.10.3 Stale Cookie Error (3)
+		 *
+		 * Cause of error
+		 * ---------------
+		 * Stale Cookie Error:  Indicates the receipt of a valid State
+		 * Cookie that has expired.
+		 */
+		len = ntohs(chunk->chunk_hdr->length);
+		*errp = sctp_make_op_error_space(asoc, chunk, len);
+		if (*errp) {
+			suseconds_t usecs = (skb->stamp.tv_sec -
+				bear_cookie->expiration.tv_sec) * 1000000L +
+				skb->stamp.tv_usec -
+				bear_cookie->expiration.tv_usec;
+
+			usecs = htonl(usecs);
+			sctp_init_cause(*errp, SCTP_ERROR_STALE_COOKIE,
+					&usecs, sizeof(usecs));
+			*error = -SCTP_IERROR_STALE_COOKIE;
+		} else
+			*error = -SCTP_IERROR_NOMEM;
+
+		goto fail;
+	}
+
+	/* Make a new base association.  */
+	scope = sctp_scope(sctp_source(chunk));
+	retval = sctp_association_new(ep, ep->base.sk, scope, gfp);
+	if (!retval) {
+		*error = -SCTP_IERROR_NOMEM;
+		goto fail;
+	}
+
+	/* Set up our peer's port number.  */
+	retval->peer.port = ntohs(chunk->sctp_hdr->source);
+
+	/* Populate the association from the cookie.  */
+	retval->c = *bear_cookie;
+
+	if (sctp_assoc_set_bind_addr_from_cookie(retval, bear_cookie,
+						 GFP_ATOMIC) < 0) {
+		*error = -SCTP_IERROR_NOMEM;
+		goto fail;
+	}
+
+	/* Also, add the destination address. */
+	if (list_empty(&retval->base.bind_addr.address_list)) {
+		sctp_add_bind_addr(&retval->base.bind_addr, &chunk->dest, 
+				   GFP_ATOMIC);
+	}
+
+	retval->next_tsn = retval->c.initial_tsn;
+	retval->ctsn_ack_point = retval->next_tsn - 1;
+
+	/* The INIT stuff will be done by the side effects.  */
+	return retval;
+
+fail:
+	if (retval)
+		sctp_association_free(retval);
+
+	return NULL;
+
+malformed:
+	/* Yikes!  The packet is either corrupt or deliberately
+	 * malformed.
+	 */
+	*error = -SCTP_IERROR_MALFORMED;
+	goto fail;
+}
+
+/********************************************************************
+ * 3rd Level Abstractions
+ ********************************************************************/
+
+struct __sctp_missing {
+	__u32 num_missing;
+	__u16 type;
+}  __attribute__((packed));;
+
+/*
+ * Report a missing mandatory parameter.
+ */
+static int sctp_process_missing_param(const struct sctp_association *asoc,
+				      sctp_param_t paramtype,
+				      sctp_chunk_t *chunk,
+				      sctp_chunk_t **errp)
+{
+	struct __sctp_missing report;
+	__u16 len;
+
+	len = WORD_ROUND(sizeof(report));
+
+	/* Make an ERROR chunk, preparing enough room for
+	 * returning multiple unknown parameters.
+	 */
+	if (!*errp)
+		*errp = sctp_make_op_error_space(asoc, chunk, len);
+
+	if (*errp) {
+		report.num_missing = htonl(1);
+		report.type = paramtype;
+		sctp_init_cause(*errp, SCTP_ERROR_INV_PARAM,
+				&report, sizeof(report));
+	}
+
+	/* Stop processing this chunk. */
+	return 0;
+}
+
+/* Report an Invalid Mandatory Parameter.  */
+static int sctp_process_inv_mandatory(const struct sctp_association *asoc,
+				      sctp_chunk_t *chunk,
+				      sctp_chunk_t **errp)
+{
+	/* Invalid Mandatory Parameter Error has no payload. */
+
+	if (!*errp)
+		*errp = sctp_make_op_error_space(asoc, chunk, 0);
+
+	if (*errp)
+		sctp_init_cause(*errp, SCTP_ERROR_INV_PARAM, NULL, 0);
+
+	/* Stop processing this chunk. */
+	return 0;
+}
+
+/* Do not attempt to handle the HOST_NAME parm.  However, do
+ * send back an indicator to the peer.
+ */
+static int sctp_process_hn_param(const struct sctp_association *asoc,
+				 union sctp_params param,
+				 sctp_chunk_t *chunk,
+				 sctp_chunk_t **errp)
+{
+	__u16 len = ntohs(param.p->length);
+
+	/* Make an ERROR chunk. */
+	if (!*errp)
+		*errp = sctp_make_op_error_space(asoc, chunk, len);
+
+	if (*errp)
+		sctp_init_cause(*errp, SCTP_ERROR_DNS_FAILED,
+				param.v, len);
+
+	/* Stop processing this chunk. */
+	return 0;
+}
+
+/* RFC 3.2.1 & the Implementers Guide 2.2.
+ *
+ * The Parameter Types are encoded such that the
+ * highest-order two bits specify the action that must be
+ * taken if the processing endpoint does not recognize the
+ * Parameter Type.
+ *
+ * 00 - Stop processing this SCTP chunk and discard it,
+ *	do not process any further chunks within it.
+ *
+ * 01 - Stop processing this SCTP chunk and discard it,
+ *	do not process any further chunks within it, and report
+ *	the unrecognized parameter in an 'Unrecognized
+ *	Parameter Type' (in either an ERROR or in the INIT ACK).
+ *
+ * 10 - Skip this parameter and continue processing.
+ *
+ * 11 - Skip this parameter and continue processing but
+ *	report the unrecognized parameter in an
+ *	'Unrecognized Parameter Type' (in either an ERROR or in
+ *	the INIT ACK).
+ *
+ * Return value:
+ * 	0 - discard the chunk
+ * 	1 - continue with the chunk
+ */
+static int sctp_process_unk_param(const struct sctp_association *asoc,
+				  union sctp_params param,
+				  sctp_chunk_t *chunk,
+				  sctp_chunk_t **errp)
+{
+	int retval = 1;
+
+	switch (param.p->type & SCTP_PARAM_ACTION_MASK) {
+	case SCTP_PARAM_ACTION_DISCARD:
+		retval =  0;
+		break;
+	case SCTP_PARAM_ACTION_DISCARD_ERR:
+		retval =  0;
+		/* Make an ERROR chunk, preparing enough room for
+		 * returning multiple unknown parameters.
+		 */
+		if (NULL == *errp)
+			*errp = sctp_make_op_error_space(asoc, chunk,
+					ntohs(chunk->chunk_hdr->length));
+
+		if (*errp)
+			sctp_init_cause(*errp, SCTP_ERROR_UNKNOWN_PARAM,
+					param.v,
+					WORD_ROUND(ntohs(param.p->length)));
+
+		break;
+	case SCTP_PARAM_ACTION_SKIP:
+		break;
+	case SCTP_PARAM_ACTION_SKIP_ERR:
+		/* Make an ERROR chunk, preparing enough room for
+		 * returning multiple unknown parameters.
+		 */
+		if (NULL == *errp)
+			*errp = sctp_make_op_error_space(asoc, chunk,
+					ntohs(chunk->chunk_hdr->length));
+
+		if (*errp) {
+			sctp_init_cause(*errp, SCTP_ERROR_UNKNOWN_PARAM,
+					param.v,
+					WORD_ROUND(ntohs(param.p->length)));
+		} else {
+			/* If there is no memory for generating the ERROR
+			 * report as specified, an ABORT will be triggered
+			 * to the peer and the association won't be
+			 * established.
+			 */
+			retval = 0;
+		}
+
+		break;
+	default:
+		break;
+	}
+
+	return retval;
+}
+
+/* Find unrecognized parameters in the chunk.
+ * Return values:
+ * 	0 - discard the chunk
+ * 	1 - continue with the chunk
+ */
+static int sctp_verify_param(const struct sctp_association *asoc,
+			     union sctp_params param,
+			     sctp_cid_t cid,
+			     sctp_chunk_t *chunk,
+			     sctp_chunk_t **err_chunk)
+{
+	int retval = 1;
+
+	/* FIXME - This routine is not looking at each parameter per the
+	 * chunk type, i.e., unrecognized parameters should be further
+	 * identified based on the chunk id.
+	 */
+
+	switch (param.p->type) {
+	case SCTP_PARAM_IPV4_ADDRESS:
+	case SCTP_PARAM_IPV6_ADDRESS:
+	case SCTP_PARAM_COOKIE_PRESERVATIVE:
+	case SCTP_PARAM_SUPPORTED_ADDRESS_TYPES:
+	case SCTP_PARAM_STATE_COOKIE:
+	case SCTP_PARAM_HEARTBEAT_INFO:
+	case SCTP_PARAM_UNRECOGNIZED_PARAMETERS:
+	case SCTP_PARAM_ECN_CAPABLE:
+		break;
+
+	case SCTP_PARAM_HOST_NAME_ADDRESS:
+		/* Tell the peer, we won't support this param.  */
+		return sctp_process_hn_param(asoc, param, chunk, err_chunk);
+	default:
+		SCTP_DEBUG_PRINTK("Unrecognized param: %d for chunk %d.\n",
+				ntohs(param.p->type), cid);
+		return sctp_process_unk_param(asoc, param, chunk, err_chunk);
+
+		break;
+	}
+	return retval;
+}
+
+/* Verify the INIT packet before we process it.  */
+int sctp_verify_init(const struct sctp_association *asoc,
+		     sctp_cid_t cid,
+		     sctp_init_chunk_t *peer_init,
+		     sctp_chunk_t *chunk,
+		     sctp_chunk_t **errp)
+{
+	union sctp_params param;
+	int has_cookie = 0;
+
+	/* Verify stream values are non-zero. */
+	if ((0 == peer_init->init_hdr.num_outbound_streams) ||
+	    (0 == peer_init->init_hdr.num_inbound_streams)) {
+
+		sctp_process_inv_mandatory(asoc, chunk, errp);
+		return 0;
+	}
+
+	/* Check for missing mandatory parameters.  */
+	sctp_walk_params(param, peer_init, init_hdr.params) {
+
+		if (SCTP_PARAM_STATE_COOKIE == param.p->type)
+			has_cookie = 1;
+
+	} /* for (loop through all parameters) */
+
+	/* The only missing mandatory param possible today is
+	 * the state cookie for an INIT-ACK chunk.
+	 */
+	if ((SCTP_CID_INIT_ACK == cid) && !has_cookie) {
+		sctp_process_missing_param(asoc, SCTP_PARAM_STATE_COOKIE,
+					   chunk, errp);
+		return 0;
+	}
+
+	/* Find unrecognized parameters. */
+
+	sctp_walk_params(param, peer_init, init_hdr.params) {
+
+		if (!sctp_verify_param(asoc, param, cid, chunk, errp))
+			return 0;
+
+	} /* for (loop through all parameters) */
+
+	return 1;
+}
+
+/* Unpack the parameters in an INIT packet into an association.
+ * Returns 0 on failure, else success.
+ * FIXME:  This is an association method.
+ */
+int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid,
+		      const union sctp_addr *peer_addr,
+		      sctp_init_chunk_t *peer_init, int gfp)
+{
+	union sctp_params param;
+	struct sctp_transport *transport;
+	struct list_head *pos, *temp;
+	char *cookie;
+
+	/* We must include the address that the INIT packet came from.
+	 * This is the only address that matters for an INIT packet.
+	 * When processing a COOKIE ECHO, we retrieve the from address
+	 * of the INIT from the cookie.
+	 */
+
+	/* This implementation defaults to making the first transport
+	 * added as the primary transport.  The source address seems to
+	 * be a a better choice than any of the embedded addresses.
+	 */
+	if (peer_addr)
+		if(!sctp_assoc_add_peer(asoc, peer_addr, gfp))
+			goto nomem;
+
+	/* Process the initialization parameters.  */
+
+	sctp_walk_params(param, peer_init, init_hdr.params) {
+
+		if (!sctp_process_param(asoc, param, peer_addr, gfp))
+                        goto clean_up;
+	}
+
+	/* The fixed INIT headers are always in network byte
+	 * order.
+	 */
+	asoc->peer.i.init_tag =
+		ntohl(peer_init->init_hdr.init_tag);
+	asoc->peer.i.a_rwnd =
+		ntohl(peer_init->init_hdr.a_rwnd);
+	asoc->peer.i.num_outbound_streams =
+		ntohs(peer_init->init_hdr.num_outbound_streams);
+	asoc->peer.i.num_inbound_streams =
+		ntohs(peer_init->init_hdr.num_inbound_streams);
+	asoc->peer.i.initial_tsn =
+		ntohl(peer_init->init_hdr.initial_tsn);
+
+	/* Apply the upper bounds for output streams based on peer's
+	 * number of inbound streams.
+	 */
+	if (asoc->c.sinit_num_ostreams  >
+	    ntohs(peer_init->init_hdr.num_inbound_streams)) {
+		asoc->c.sinit_num_ostreams =
+			ntohs(peer_init->init_hdr.num_inbound_streams);
+	}
+
+	if (asoc->c.sinit_max_instreams >
+	    ntohs(peer_init->init_hdr.num_outbound_streams)) {
+		asoc->c.sinit_max_instreams =
+			ntohs(peer_init->init_hdr.num_outbound_streams);
+	}
+
+	/* Copy Initiation tag from INIT to VT_peer in cookie.   */
+	asoc->c.peer_vtag = asoc->peer.i.init_tag;
+
+	/* Peer Rwnd   : Current calculated value of the peer's rwnd.  */
+	asoc->peer.rwnd = asoc->peer.i.a_rwnd;
+
+	/* Copy cookie in case we need to resend COOKIE-ECHO. */
+	cookie = asoc->peer.cookie;
+	if (cookie) {
+		asoc->peer.cookie = kmalloc(asoc->peer.cookie_len, gfp);
+		if (!asoc->peer.cookie)
+			goto clean_up;
+		memcpy(asoc->peer.cookie, cookie, asoc->peer.cookie_len);
+	}
+
+	/* RFC 2960 7.2.1 The initial value of ssthresh MAY be arbitrarily
+	 * high (for example, implementations MAY use the size of the receiver
+	 * advertised window).
+	 */
+	list_for_each(pos, &asoc->peer.transport_addr_list) {
+		transport = list_entry(pos, struct sctp_transport, transports);
+		transport->ssthresh = asoc->peer.i.a_rwnd;
+	}
+
+	/* Set up the TSN tracking pieces.  */
+	sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_SIZE,
+			 asoc->peer.i.initial_tsn);
+
+	/* RFC 2960 6.5 Stream Identifier and Stream Sequence Number
+	 *
+	 * The stream sequence number in all the streams shall start
+	 * from 0 when the association is established.  Also, when the
+	 * stream sequence number reaches the value 65535 the next
+	 * stream sequence number shall be set to 0.
+	 */
+
+	/* Allocate storage for the negotiated streams. */
+	asoc->ssnmap = sctp_ssnmap_new(asoc->peer.i.num_outbound_streams,
+				       asoc->c.sinit_num_ostreams, gfp);
+	if (!asoc->ssnmap)
+		goto nomem_ssnmap;
+
+	/* ADDIP Section 4.1 ASCONF Chunk Procedures
+	 *
+	 * When an endpoint has an ASCONF signaled change to be sent to the
+	 * remote endpoint it should do the following:
+	 * ...
+	 * A2) A serial number should be assigned to the Chunk. The serial
+	 * number should be a monotonically increasing number. All serial
+	 * numbers are defined to be initialized at the start of the
+	 * association to the same value as the Initial TSN.
+	 */
+	asoc->peer.addip_serial = asoc->peer.i.initial_tsn - 1;
+	return 1;
+
+nomem_ssnmap:
+clean_up:
+	/* Release the transport structures. */
+	list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
+		transport = list_entry(pos, struct sctp_transport, transports);
+		list_del(pos);
+		sctp_transport_free(transport);
+	}
+nomem:
+	return 0;
+}
+
+
+/* Update asoc with the option described in param.
+ *
+ * RFC2960 3.3.2.1 Optional/Variable Length Parameters in INIT
+ *
+ * asoc is the association to update.
+ * param is the variable length parameter to use for update.
+ * cid tells us if this is an INIT, INIT ACK or COOKIE ECHO.
+ * If the current packet is an INIT we want to minimize the amount of
+ * work we do.  In particular, we should not build transport
+ * structures for the addresses.
+ */
+int sctp_process_param(struct sctp_association *asoc, union sctp_params param,
+		       const union sctp_addr *peer_addr, int gfp)
+{
+	union sctp_addr addr;
+	int i;
+	__u16 sat;
+	int retval = 1;
+	sctp_scope_t scope;
+	time_t stale;
+
+	/* We maintain all INIT parameters in network byte order all the
+	 * time.  This allows us to not worry about whether the parameters
+	 * came from a fresh INIT, and INIT ACK, or were stored in a cookie.
+	 */
+	switch (param.p->type) {
+	case SCTP_PARAM_IPV6_ADDRESS:
+		if( PF_INET6 != asoc->base.sk->family)
+			break;
+		/* Fall through. */
+	case SCTP_PARAM_IPV4_ADDRESS:
+		sctp_param2sockaddr(&addr, param.addr, asoc->peer.port, 0);
+		scope = sctp_scope(peer_addr);
+		if (sctp_in_scope(&addr, scope))
+			if (!sctp_assoc_add_peer(asoc, &addr, gfp))
+				return 0;
+		break;
+
+	case SCTP_PARAM_COOKIE_PRESERVATIVE:
+		if (!sctp_proto.cookie_preserve_enable)
+			break;
+
+		stale = ntohl(param.life->lifespan_increment);
+
+		/* Suggested Cookie Life span increment's unit is msec,
+		 * (1/1000sec).
+		 */
+		asoc->cookie_life.tv_sec += stale / 1000;
+		asoc->cookie_life.tv_usec += (stale % 1000) * 1000;
+		break;
+
+	case SCTP_PARAM_HOST_NAME_ADDRESS:
+		SCTP_DEBUG_PRINTK("unimplemented SCTP_HOST_NAME_ADDRESS\n");
+		break;
+
+	case SCTP_PARAM_SUPPORTED_ADDRESS_TYPES:
+		/* Turn off the default values first so we'll know which
+		 * ones are really set by the peer.
+		 */
+		asoc->peer.ipv4_address = 0;
+		asoc->peer.ipv6_address = 0;
+
+		/* Cycle through address types; avoid divide by 0. */
+		sat = ntohs(param.p->length) - sizeof(sctp_paramhdr_t);
+		if (sat)
+			sat /= sizeof(__u16);
+
+		for (i = 0; i < sat; ++i) {
+			switch (param.sat->types[i]) {
+			case SCTP_PARAM_IPV4_ADDRESS:
+				asoc->peer.ipv4_address = 1;
+				break;
+
+			case SCTP_PARAM_IPV6_ADDRESS:
+				asoc->peer.ipv6_address = 1;
+				break;
+
+			case SCTP_PARAM_HOST_NAME_ADDRESS:
+				asoc->peer.hostname_address = 1;
+				break;
+
+			default: /* Just ignore anything else.  */
+				break;
+			};
+		}
+		break;
+
+	case SCTP_PARAM_STATE_COOKIE:
+		asoc->peer.cookie_len =
+			ntohs(param.p->length) - sizeof(sctp_paramhdr_t);
+		asoc->peer.cookie = param.cookie->body;
+		break;
+
+	case SCTP_PARAM_HEARTBEAT_INFO:
+		/* Would be odd to receive, but it causes no problems. */
+		break;
+
+	case SCTP_PARAM_UNRECOGNIZED_PARAMETERS:
+		/* Rejected during verify stage. */
+		break;
+
+	case SCTP_PARAM_ECN_CAPABLE:
+		asoc->peer.ecn_capable = 1;
+		break;
+
+	default:
+		/* Any unrecognized parameters should have been caught
+		 * and handled by sctp_verify_param() which should be
+		 * called prior to this routine.  Simply log the error
+		 * here.
+		 */
+		SCTP_DEBUG_PRINTK("Ignoring param: %d for association %p.\n",
+				  ntohs(param.p->type), asoc);
+		break;
+	};
+
+	return retval;
+}
+
+/* Select a new verification tag.  */
+__u32 sctp_generate_tag(const struct sctp_endpoint *ep)
+{
+	/* I believe that this random number generator complies with RFC1750.
+	 * A tag of 0 is reserved for special cases (e.g. INIT).
+	 */
+	__u32 x;
+
+	do {
+		get_random_bytes(&x, sizeof(__u32));
+	} while (x == 0);
+
+	return x;
+}
+
+/* Select an initial TSN to send during startup.  */
+__u32 sctp_generate_tsn(const struct sctp_endpoint *ep)
+{
+	__u32 retval;
+
+	get_random_bytes(&retval, sizeof(__u32));
+	return retval;
+}
+
+/********************************************************************
+ * 4th Level Abstractions
+ ********************************************************************/
+
+/* Convert from an SCTP IP parameter to a union sctp_addr.  */
+void sctp_param2sockaddr(union sctp_addr *addr, sctp_addr_param_t *param,
+			 __u16 port, int iif)
+{
+	switch(param->v4.param_hdr.type) {
+	case SCTP_PARAM_IPV4_ADDRESS:
+		addr->v4.sin_family = AF_INET;
+		addr->v4.sin_port = port;
+		addr->v4.sin_addr.s_addr = param->v4.addr.s_addr;
+		break;
+
+	case SCTP_PARAM_IPV6_ADDRESS:
+		addr->v6.sin6_family = AF_INET6;
+		addr->v6.sin6_port = port;
+		addr->v6.sin6_flowinfo = 0; /* BUG */
+		addr->v6.sin6_addr = param->v6.addr;
+		addr->v6.sin6_scope_id = iif;
+		break;
+
+	default:
+		SCTP_DEBUG_PRINTK("Illegal address type %d\n",
+				  ntohs(param->v4.param_hdr.type));
+		break;
+	};
+}
+
+/* Convert an IP address in an SCTP param into a sockaddr_in.  */
+/* Returns true if a valid conversion was possible.  */
+int sctp_addr2sockaddr(union sctp_params p, union sctp_addr *sa)
+{
+	switch (p.p->type) {
+	case SCTP_PARAM_IPV4_ADDRESS:
+		sa->v4.sin_addr = *((struct in_addr *)&p.v4->addr);
+		sa->v4.sin_family = AF_INET;
+		break;
+
+	case SCTP_PARAM_IPV6_ADDRESS:
+		*((struct in6_addr *)&sa->v4.sin_addr)
+			= p.v6->addr;
+		sa->v4.sin_family = AF_INET6;
+		break;
+
+        default:
+                return 0;
+        };
+
+	return 1;
+}
+
+/* Convert a sockaddr_in to an IP address in an SCTP param.
+ * Returns len if a valid conversion was possible.
+ */
+int sockaddr2sctp_addr(const union sctp_addr *sa, sctp_addr_param_t *p)
+{
+	int len = 0;
+
+	switch (sa->v4.sin_family) {
+	case AF_INET:
+		p->v4.param_hdr.type = SCTP_PARAM_IPV4_ADDRESS;
+		p->v4.param_hdr.length = ntohs(sizeof(sctp_ipv4addr_param_t));
+		len = sizeof(sctp_ipv4addr_param_t);
+		p->v4.addr.s_addr = sa->v4.sin_addr.s_addr;
+		break;
+
+	case AF_INET6:
+		p->v6.param_hdr.type = SCTP_PARAM_IPV6_ADDRESS;
+		p->v6.param_hdr.length = ntohs(sizeof(sctp_ipv6addr_param_t));
+		len = sizeof(sctp_ipv6addr_param_t);
+		p->v6.addr = *(&sa->v6.sin6_addr);
+		break;
+
+	default:
+		printk(KERN_WARNING "sockaddr2sctp_addr: Illegal family %d.\n",
+		       sa->v4.sin_family);
+		return 0;
+	};
+
+	return len;
+}
diff -urN linux-2.4.23-pre5/net/sctp/sm_sideeffect.c linux-2.4.23-pre6/net/sctp/sm_sideeffect.c
--- linux-2.4.23-pre5/net/sctp/sm_sideeffect.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/sm_sideeffect.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,1185 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * These functions work with the state functions in sctp_sm_statefuns.c
+ * to implement that state operations.  These functions implement the
+ * steps which require modifying existing data structures.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Karl Knutson          <karl@athena.chicago.il.us>
+ *    Jon Grimm             <jgrimm@austin.ibm.com>
+ *    Hui Huang		    <hui.huang@nokia.com>
+ *    Dajiang Zhang	    <dajiang.zhang@nokia.com>
+ *    Daisy Chang	    <daisyc@us.ibm.com>
+ *    Sridhar Samudrala	    <sri@us.ibm.com>
+ *    Ardelle Fan	    <ardelle.fan@intel.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/ip.h>
+#include <net/sock.h>
+#include <net/sctp/sctp.h>
+#include <net/sctp/sm.h>
+
+/********************************************************************
+ * Helper functions
+ ********************************************************************/
+
+/* A helper function for delayed processing of INET ECN CE bit. */
+static void sctp_do_ecn_ce_work(struct sctp_association *asoc, 
+				__u32 lowest_tsn)
+{
+	/* Save the TSN away for comparison when we receive CWR */
+
+	asoc->last_ecne_tsn = lowest_tsn;
+	asoc->need_ecne = 1;
+}
+
+/* Helper function for delayed processing of SCTP ECNE chunk.  */
+/* RFC 2960 Appendix A
+ *
+ * RFC 2481 details a specific bit for a sender to send in
+ * the header of its next outbound TCP segment to indicate to
+ * its peer that it has reduced its congestion window.  This
+ * is termed the CWR bit.  For SCTP the same indication is made
+ * by including the CWR chunk.  This chunk contains one data
+ * element, i.e. the TSN number that was sent in the ECNE chunk.
+ * This element represents the lowest TSN number in the datagram
+ * that was originally marked with the CE bit.
+ */
+static sctp_chunk_t *sctp_do_ecn_ecne_work(struct sctp_association *asoc,
+					   __u32 lowest_tsn,
+					   sctp_chunk_t *chunk)
+{
+	sctp_chunk_t *repl;
+
+	/* Our previously transmitted packet ran into some congestion
+	 * so we should take action by reducing cwnd and ssthresh
+	 * and then ACK our peer that we we've done so by
+	 * sending a CWR.
+	 */
+
+	/* First, try to determine if we want to actually lower
+	 * our cwnd variables.  Only lower them if the ECNE looks more
+	 * recent than the last response.
+	 */
+	if (TSN_lt(asoc->last_cwr_tsn, lowest_tsn)) {
+		struct sctp_transport *transport;
+
+		/* Find which transport's congestion variables
+		 * need to be adjusted.
+		 */
+		transport = sctp_assoc_lookup_tsn(asoc, lowest_tsn);
+
+		/* Update the congestion variables. */
+		if (transport)
+			sctp_transport_lower_cwnd(transport,
+						  SCTP_LOWER_CWND_ECNE);
+		asoc->last_cwr_tsn = lowest_tsn;
+	}
+
+	/* Always try to quiet the other end.  In case of lost CWR,
+	 * resend last_cwr_tsn.
+	 */
+	repl = sctp_make_cwr(asoc, asoc->last_cwr_tsn, chunk);
+
+	/* If we run out of memory, it will look like a lost CWR.  We'll
+	 * get back in sync eventually.
+	 */
+	return repl;
+}
+
+/* Helper function to do delayed processing of ECN CWR chunk.  */
+static void sctp_do_ecn_cwr_work(struct sctp_association *asoc,
+				 __u32 lowest_tsn)
+{
+	/* Turn off ECNE getting auto-prepended to every outgoing
+	 * packet
+	 */
+	asoc->need_ecne = 0;
+}
+
+/* Generate SACK if necessary.  We call this at the end of a packet.  */
+int sctp_gen_sack(struct sctp_association *asoc, int force,
+		  sctp_cmd_seq_t *commands)
+{
+	__u32 ctsn, max_tsn_seen;
+	struct sctp_chunk *sack;
+	int error = 0;
+
+	if (force)
+		asoc->peer.sack_needed = 1;
+
+	ctsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map);
+	max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map);
+
+	/* From 12.2 Parameters necessary per association (i.e. the TCB):
+	 *
+	 * Ack State : This flag indicates if the next received packet
+	 * 	     : is to be responded to with a SACK. ...
+	 *	     : When DATA chunks are out of order, SACK's
+	 *           : are not delayed (see Section 6).
+	 *
+	 * [This is actually not mentioned in Section 6, but we
+	 * implement it here anyway. --piggy]
+	 */
+        if (max_tsn_seen != ctsn)
+		asoc->peer.sack_needed = 1;
+
+	/* From 6.2  Acknowledgement on Reception of DATA Chunks:
+	 *
+	 * Section 4.2 of [RFC2581] SHOULD be followed. Specifically,
+	 * an acknowledgement SHOULD be generated for at least every
+	 * second packet (not every second DATA chunk) received, and
+	 * SHOULD be generated within 200 ms of the arrival of any
+	 * unacknowledged DATA chunk. ...
+	 */
+	if (!asoc->peer.sack_needed) {
+		/* We will need a SACK for the next packet.  */
+		asoc->peer.sack_needed = 1;
+		goto out;
+	} else {
+		if (asoc->a_rwnd > asoc->rwnd)
+			asoc->a_rwnd = asoc->rwnd;
+		sack = sctp_make_sack(asoc);
+		if (!sack)
+			goto nomem;
+
+		asoc->peer.sack_needed = 0;
+
+		error = sctp_outq_tail(&asoc->outqueue, sack);
+
+		/* Stop the SACK timer.  */
+		sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+				SCTP_TO(SCTP_EVENT_TIMEOUT_SACK));
+	}
+out:
+	return error;
+nomem:
+	error = -ENOMEM;
+	return error;
+}
+
+/* When the T3-RTX timer expires, it calls this function to create the
+ * relevant state machine event.
+ */
+void sctp_generate_t3_rtx_event(unsigned long peer)
+{
+	int error;
+	struct sctp_transport *transport = (struct sctp_transport *) peer;
+	struct sctp_association *asoc = transport->asoc;
+
+	/* Check whether a task is in the sock.  */
+
+	sctp_bh_lock_sock(asoc->base.sk);
+	if (sock_owned_by_user(asoc->base.sk)) {
+		SCTP_DEBUG_PRINTK("%s:Sock is busy.\n", __FUNCTION__);
+
+		/* Try again later.  */
+		if (!mod_timer(&transport->T3_rtx_timer, jiffies + (HZ/20)))
+			sctp_transport_hold(transport);
+		goto out_unlock;
+	}
+
+	/* Is this transport really dead and just waiting around for
+	 * the timer to let go of the reference?
+	 */
+	if (transport->dead)
+		goto out_unlock;
+
+	/* Run through the state machine.  */
+	error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT,
+			   SCTP_ST_TIMEOUT(SCTP_EVENT_TIMEOUT_T3_RTX),
+			   asoc->state,
+			   asoc->ep, asoc,
+			   transport, GFP_ATOMIC);
+
+	if (error)
+		asoc->base.sk->err = -error;
+
+out_unlock:
+	sctp_bh_unlock_sock(asoc->base.sk);
+	sctp_transport_put(transport);
+}
+
+/* This is a sa interface for producing timeout events.  It works
+ * for timeouts which use the association as their parameter.
+ */
+static void sctp_generate_timeout_event(struct sctp_association *asoc,
+					sctp_event_timeout_t timeout_type)
+{
+	int error = 0;
+
+	sctp_bh_lock_sock(asoc->base.sk);
+	if (sock_owned_by_user(asoc->base.sk)) {
+		SCTP_DEBUG_PRINTK("%s:Sock is busy: timer %d\n",
+				  __FUNCTION__,
+				  timeout_type);
+
+		/* Try again later.  */
+		if (!mod_timer(&asoc->timers[timeout_type], jiffies + (HZ/20)))
+			sctp_association_hold(asoc);
+		goto out_unlock;
+	}
+
+	/* Is this association really dead and just waiting around for
+	 * the timer to let go of the reference?
+	 */
+	if (asoc->base.dead)
+		goto out_unlock;
+
+	/* Run through the state machine.  */
+	error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT,
+			   SCTP_ST_TIMEOUT(timeout_type),
+			   asoc->state, asoc->ep, asoc,
+			   (void *)timeout_type, GFP_ATOMIC);
+
+	if (error)
+		asoc->base.sk->err = -error;
+
+out_unlock:
+	sctp_bh_unlock_sock(asoc->base.sk);
+	sctp_association_put(asoc);
+}
+
+void sctp_generate_t1_cookie_event(unsigned long data)
+{
+	struct sctp_association *asoc = (struct sctp_association *) data;
+	sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T1_COOKIE);
+}
+
+void sctp_generate_t1_init_event(unsigned long data)
+{
+	struct sctp_association *asoc = (struct sctp_association *) data;
+	sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T1_INIT);
+}
+
+void sctp_generate_t2_shutdown_event(unsigned long data)
+{
+	struct sctp_association *asoc = (struct sctp_association *) data;
+	sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T2_SHUTDOWN);
+}
+
+void sctp_generate_t5_shutdown_guard_event(unsigned long data)
+{
+        struct sctp_association *asoc = (struct sctp_association *)data;
+        sctp_generate_timeout_event(asoc,
+				    SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD);
+
+} /* sctp_generate_t5_shutdown_guard_event() */
+
+void sctp_generate_autoclose_event(unsigned long data)
+{
+	struct sctp_association *asoc = (struct sctp_association *) data;
+	sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_AUTOCLOSE);
+}
+
+/* Generate a heart beat event.  If the sock is busy, reschedule.   Make
+ * sure that the transport is still valid.
+ */
+void sctp_generate_heartbeat_event(unsigned long data)
+{
+	int error = 0;
+	struct sctp_transport *transport = (struct sctp_transport *) data;
+	struct sctp_association *asoc = transport->asoc;
+
+	sctp_bh_lock_sock(asoc->base.sk);
+	if (sock_owned_by_user(asoc->base.sk)) {
+		SCTP_DEBUG_PRINTK("%s:Sock is busy.\n", __FUNCTION__);
+
+		/* Try again later.  */
+		if (!mod_timer(&transport->hb_timer, jiffies + (HZ/20)))
+			sctp_transport_hold(transport);
+		goto out_unlock;
+	}
+
+	/* Is this structure just waiting around for us to actually
+	 * get destroyed?
+	 */
+	if (transport->dead)
+		goto out_unlock;
+
+	error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT,
+			   SCTP_ST_TIMEOUT(SCTP_EVENT_TIMEOUT_HEARTBEAT),
+			   asoc->state, asoc->ep, asoc,
+			   transport, GFP_ATOMIC);
+
+         if (error)
+		 asoc->base.sk->err = -error;
+
+out_unlock:
+	sctp_bh_unlock_sock(asoc->base.sk);
+	sctp_transport_put(transport);
+}
+
+/* Inject a SACK Timeout event into the state machine.  */
+void sctp_generate_sack_event(unsigned long data)
+{
+	struct sctp_association *asoc = (struct sctp_association *) data;
+	sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_SACK);
+}
+
+sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = {
+	NULL,
+	sctp_generate_t1_cookie_event,
+	sctp_generate_t1_init_event,
+	sctp_generate_t2_shutdown_event,
+	NULL,
+	sctp_generate_t5_shutdown_guard_event,
+	sctp_generate_heartbeat_event,
+	sctp_generate_sack_event,
+	sctp_generate_autoclose_event,
+};
+
+
+/* RFC 2960 8.2 Path Failure Detection
+ *
+ * When its peer endpoint is multi-homed, an endpoint should keep a
+ * error counter for each of the destination transport addresses of the
+ * peer endpoint.
+ *
+ * Each time the T3-rtx timer expires on any address, or when a
+ * HEARTBEAT sent to an idle address is not acknowledged within a RTO,
+ * the error counter of that destination address will be incremented.
+ * When the value in the error counter exceeds the protocol parameter
+ * 'Path.Max.Retrans' of that destination address, the endpoint should
+ * mark the destination transport address as inactive, and a
+ * notification SHOULD be sent to the upper layer.
+ *
+ */
+static void sctp_do_8_2_transport_strike(struct sctp_association *asoc,
+					 struct sctp_transport *transport)
+{
+	/* The check for association's overall error counter exceeding the
+	 * threshold is done in the state function.
+	 */
+	asoc->overall_error_count++;
+
+	if (transport->active &&
+	    (transport->error_count++ >= transport->error_threshold)) {
+		SCTP_DEBUG_PRINTK("transport_strike: transport "
+				  "IP:%d.%d.%d.%d failed.\n",
+				  NIPQUAD(transport->ipaddr.v4.sin_addr));
+		sctp_assoc_control_transport(asoc, transport,
+					     SCTP_TRANSPORT_DOWN,
+					     SCTP_FAILED_THRESHOLD);
+	}
+
+	/* E2) For the destination address for which the timer
+	 * expires, set RTO <- RTO * 2 ("back off the timer").  The
+	 * maximum value discussed in rule C7 above (RTO.max) may be
+	 * used to provide an upper bound to this doubling operation.
+	 */
+	transport->rto = min((transport->rto * 2), transport->asoc->rto_max);
+}
+
+/* Worker routine to handle INIT command failure.  */
+static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands,
+				 struct sctp_association *asoc,
+				 unsigned error)
+{
+	struct sctp_ulpevent *event;
+
+	event = sctp_ulpevent_make_assoc_change(asoc,0, SCTP_CANT_STR_ASSOC,
+						0, 0, 0, GFP_ATOMIC);
+
+	if (event)
+		sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
+				SCTP_ULPEVENT(event));
+
+	/* SEND_FAILED sent later when cleaning up the association. */
+	asoc->outqueue.error = error;
+	sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
+}
+
+/* Worker routine to handle SCTP_CMD_ASSOC_FAILED.  */
+static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands,
+				  struct sctp_association *asoc,
+				  sctp_event_t event_type,
+				  sctp_subtype_t subtype,
+				  struct sctp_chunk *chunk,
+				  unsigned error)
+{
+	struct sctp_ulpevent *event;
+
+	/* 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,
+						GFP_ATOMIC);
+	if (event)
+		sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
+				SCTP_ULPEVENT(event));
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+			SCTP_STATE(SCTP_STATE_CLOSED));
+
+	/* SEND_FAILED sent later when cleaning up the association. */
+	asoc->outqueue.error = error;
+	sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
+}
+
+/* Process an init chunk (may be real INIT/INIT-ACK or an embedded INIT
+ * inside the cookie.  In reality, this is only used for INIT-ACK processing
+ * since all other cases use "temporary" associations and can do all
+ * their work in statefuns directly.
+ */
+static int sctp_cmd_process_init(sctp_cmd_seq_t *commands,
+				 struct sctp_association *asoc,
+				 struct sctp_chunk *chunk,
+				 sctp_init_chunk_t *peer_init, int gfp)
+{
+	int error;
+
+	/* We only process the init as a sideeffect in a single
+	 * case.   This is when we process the INIT-ACK.   If we
+	 * fail during INIT processing (due to malloc problems),
+	 * just return the error and stop processing the stack.
+	 */
+	if (!sctp_process_init(asoc, chunk->chunk_hdr->type,
+			       sctp_source(chunk), peer_init, gfp))
+		error = -ENOMEM;
+	else
+		error = 0;
+
+	return error;
+}
+
+/* Helper function to break out starting up of heartbeat timers.  */
+static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *cmds,
+				     struct sctp_association *asoc)
+{
+	struct sctp_transport *t;
+	struct list_head *pos;
+
+	/* Start a heartbeat timer for each transport on the association.
+	 * hold a reference on the transport to make sure none of
+	 * the needed data structures go away.
+	 */
+	list_for_each(pos, &asoc->peer.transport_addr_list) {
+		t = list_entry(pos, struct sctp_transport, transports);
+
+		if (!mod_timer(&t->hb_timer, sctp_transport_timeout(t)))
+			sctp_transport_hold(t);
+	}
+}
+
+static void sctp_cmd_hb_timers_stop(sctp_cmd_seq_t *cmds,
+				    struct sctp_association *asoc)
+{
+	struct sctp_transport *t;
+	struct list_head *pos;
+
+	/* Stop all heartbeat timers. */
+
+	list_for_each(pos, &asoc->peer.transport_addr_list) {
+		t = list_entry(pos, struct sctp_transport, transports);
+		if (del_timer(&t->hb_timer))
+			sctp_transport_put(t);
+	}
+}
+
+/* Helper function to update the heartbeat timer. */
+static void sctp_cmd_hb_timer_update(sctp_cmd_seq_t *cmds,
+				     struct sctp_association *asoc,
+				     struct sctp_transport *t)
+{
+	/* Update the heartbeat timer.  */
+	if (!mod_timer(&t->hb_timer, sctp_transport_timeout(t)))
+		sctp_transport_hold(t);
+}
+
+/* Helper function to handle the reception of an HEARTBEAT ACK.  */
+static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds,
+				  struct sctp_association *asoc,
+				  struct sctp_transport *t,
+				  sctp_chunk_t *chunk)
+{
+	sctp_sender_hb_info_t *hbinfo;
+
+	/* 8.3 Upon the receipt of the HEARTBEAT ACK, the sender of the
+	 * HEARTBEAT should clear the error counter of the destination
+	 * transport address to which the HEARTBEAT was sent.
+	 * The association's overall error count is also cleared.
+	 */
+	t->error_count = 0;
+	t->asoc->overall_error_count = 0;
+
+	/* Mark the destination transport address as active if it is not so
+	 * marked.
+	 */
+	if (!t->active)
+		sctp_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP,
+					     SCTP_HEARTBEAT_SUCCESS);
+
+	/* The receiver of the HEARTBEAT ACK should also perform an
+	 * RTT measurement for that destination transport address
+	 * using the time value carried in the HEARTBEAT ACK chunk.
+	 */
+	hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data;
+	sctp_transport_update_rto(t, (jiffies - hbinfo->sent_at));
+}
+
+/* Helper function to do a transport reset at the expiry of the hearbeat
+ * timer.
+ */
+static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds,
+				     struct sctp_association *asoc,
+				     struct sctp_transport *t)
+{
+	sctp_transport_lower_cwnd(t, SCTP_LOWER_CWND_INACTIVE);
+
+	/* Mark one strike against a transport.  */
+	sctp_do_8_2_transport_strike(asoc, t);
+}
+
+/* Helper function to process the process SACK command.  */
+static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds,
+				 struct sctp_association *asoc,
+				 sctp_sackhdr_t *sackh)
+{
+	int err;
+
+	if (sctp_outq_sack(&asoc->outqueue, sackh)) {
+		/* There are no more TSNs awaiting SACK.  */
+		err = sctp_do_sm(SCTP_EVENT_T_OTHER,
+				 SCTP_ST_OTHER(SCTP_EVENT_NO_PENDING_TSN),
+				 asoc->state, asoc->ep, asoc, NULL,
+				 GFP_ATOMIC);
+	} else {
+		/* Windows may have opened, so we need
+		 * to check if we have DATA to transmit
+		 */
+		err = sctp_outq_flush(&asoc->outqueue, 0);
+	}
+
+	return err;
+}
+
+/* Helper function to set the timeout value for T2-SHUTDOWN timer and to set
+ * the transport for a shutdown chunk.
+ */
+static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, 
+			      struct sctp_association *asoc,
+			      sctp_chunk_t *chunk)
+{
+	struct sctp_transport *t;
+
+	t = sctp_assoc_choose_shutdown_transport(asoc);
+	asoc->shutdown_last_sent_to = t;
+	asoc->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = t->rto;
+	chunk->transport = t;
+}
+
+/* Helper function to change the state of an association. */
+static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, struct sctp_association *asoc,
+			       sctp_state_t state)
+{
+
+	struct sock *sk = asoc->base.sk;
+	struct sctp_opt *sp = sctp_sk(sk);
+
+	asoc->state = state;
+	asoc->state_timestamp = jiffies;
+
+	if (SCTP_SOCKET_TCP == sp->type) {
+		/* Change the sk->state of a TCP-style socket that has 
+		 * sucessfully completed a connect() call.
+		 */
+		if ((SCTP_STATE_ESTABLISHED == asoc->state) &&
+		    (SCTP_SS_CLOSED == sk->state))
+			sk->state = SCTP_SS_ESTABLISHED;
+
+		/* Set the RCV_SHUTDOWN flag when a SHUTDOWN is received. */
+		if (SCTP_STATE_SHUTDOWN_RECEIVED == asoc->state)
+			sk->shutdown |= RCV_SHUTDOWN;
+
+	}
+
+	if ((SCTP_STATE_ESTABLISHED == asoc->state) ||
+	    (SCTP_STATE_CLOSED == asoc->state) ||
+	    (SCTP_STATE_SHUTDOWN_RECEIVED == asoc->state)) {
+		/* Wake up any processes waiting in the asoc's wait queue in
+		 * sctp_wait_for_connect() or sctp_wait_for_sndbuf().
+	 	 */
+		if (waitqueue_active(&asoc->wait))
+			wake_up_interruptible(&asoc->wait);
+
+		/* Wake up any processes waiting in the sk's sleep queue of
+		 * a TCP-style or UDP-style peeled-off socket in
+		 * sctp_wait_for_accept() or sctp_wait_for_packet().
+		 * For a UDP-style socket, the waiters are woken up by the
+		 * notifications.
+		 */
+		if (SCTP_SOCKET_UDP != sp->type)
+			sk->state_change(sk);
+	}
+
+}
+
+/* Helper function to delete an association. */
+static void sctp_cmd_delete_tcb(sctp_cmd_seq_t *cmds,
+			      struct sctp_association *asoc)
+{
+	struct sock *sk = asoc->base.sk;
+
+	/* If it is a non-temporary association belonging to a TCP-style
+	 * listening socket, do not free it so that accept() can pick it
+	 * up later.
+	 */
+	if ((SCTP_SOCKET_TCP == sctp_sk(sk)->type) &&
+	    (SCTP_SS_LISTENING == sk->state) && (!asoc->temp))
+		return;
+
+	sctp_unhash_established(asoc);
+	sctp_association_free(asoc);
+}
+
+/* These three macros allow us to pull the debugging code out of the
+ * main flow of sctp_do_sm() to keep attention focused on the real
+ * functionality there.
+ */
+#define DEBUG_PRE \
+	SCTP_DEBUG_PRINTK("sctp_do_sm prefn: " \
+			  "ep %p, %s, %s, asoc %p[%s], %s\n", \
+			  ep, sctp_evttype_tbl[event_type], \
+			  (*debug_fn)(subtype), asoc, \
+			  sctp_state_tbl[state], state_fn->name)
+
+#define DEBUG_POST \
+	SCTP_DEBUG_PRINTK("sctp_do_sm postfn: " \
+			  "asoc %p, status: %s\n", \
+			  asoc, sctp_status_tbl[status])
+
+#define DEBUG_POST_SFX \
+	SCTP_DEBUG_PRINTK("sctp_do_sm post sfx: error %d, asoc %p[%s]\n", \
+			  error, asoc, \
+			  sctp_state_tbl[(asoc && sctp_id2assoc(ep->base.sk, \
+			  sctp_assoc2id(asoc)))?asoc->state:SCTP_STATE_CLOSED])
+
+/*
+ * This is the master state machine processing function.
+ *
+ * If you want to understand all of lksctp, this is a
+ * good place to start.
+ */
+int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype,
+	       sctp_state_t state,
+	       struct sctp_endpoint *ep,
+	       struct sctp_association *asoc,
+	       void *event_arg,
+	       int gfp)
+{
+	sctp_cmd_seq_t commands;
+	sctp_sm_table_entry_t *state_fn;
+	sctp_disposition_t status;
+	int error = 0;
+	typedef const char *(printfn_t)(sctp_subtype_t);
+
+	static printfn_t *table[] = {
+		NULL, sctp_cname, sctp_tname, sctp_oname, sctp_pname,
+	};
+	printfn_t *debug_fn  __attribute__ ((unused)) = table[event_type];
+
+	/* Look up the state function, run it, and then process the
+	 * side effects.  These three steps are the heart of lksctp.
+	 */
+	state_fn = sctp_sm_lookup_event(event_type, state, subtype);
+
+	sctp_init_cmd_seq(&commands);
+
+	DEBUG_PRE;
+	status = (*state_fn->fn)(ep, asoc, subtype, event_arg, &commands);
+	DEBUG_POST;
+
+	error = sctp_side_effects(event_type, subtype, state,
+				  ep, asoc, event_arg, status, 
+				  &commands, gfp);
+	DEBUG_POST_SFX;
+
+	return error;
+}
+
+#undef DEBUG_PRE
+#undef DEBUG_POST
+
+/*****************************************************************
+ * This the master state function side effect processing function.
+ *****************************************************************/
+int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
+		      sctp_state_t state,
+		      struct sctp_endpoint *ep,
+		      struct sctp_association *asoc,
+		      void *event_arg,
+		      sctp_disposition_t status,
+		      sctp_cmd_seq_t *commands,
+		      int gfp)
+{
+	int error;
+
+	/* FIXME - Most of the dispositions left today would be categorized
+	 * as "exceptional" dispositions.  For those dispositions, it
+	 * may not be proper to run through any of the commands at all.
+	 * For example, the command interpreter might be run only with
+	 * disposition SCTP_DISPOSITION_CONSUME.
+	 */
+	if (0 != (error = sctp_cmd_interpreter(event_type, subtype, state,
+					       ep, asoc,
+					       event_arg, status,
+					       commands, gfp)))
+		goto bail;
+
+	switch (status) {
+	case SCTP_DISPOSITION_DISCARD:
+		SCTP_DEBUG_PRINTK("Ignored sctp protocol event - state %d, "
+				  "event_type %d, event_id %d\n",
+				  state, event_type, subtype.chunk);
+		break;
+
+	case SCTP_DISPOSITION_NOMEM:
+		/* We ran out of memory, so we need to discard this
+		 * packet.
+		 */
+		/* BUG--we should now recover some memory, probably by
+		 * reneging...
+		 */
+		error = -ENOMEM;
+		break;
+
+        case SCTP_DISPOSITION_DELETE_TCB:
+		/* This should now be a command. */
+		break;
+
+	case SCTP_DISPOSITION_CONSUME:
+	case SCTP_DISPOSITION_ABORT:
+		/*
+		 * We should no longer have much work to do here as the
+		 * real work has been done as explicit commands above.
+		 */
+		break;
+
+	case SCTP_DISPOSITION_VIOLATION:
+		printk(KERN_ERR "sctp protocol violation state %d "
+		       "chunkid %d\n", state, subtype.chunk);
+		break;
+
+	case SCTP_DISPOSITION_NOT_IMPL:
+		printk(KERN_WARNING "sctp unimplemented feature in state %d, "
+		       "event_type %d, event_id %d\n",
+		       state, event_type, subtype.chunk);
+		break;
+
+	case SCTP_DISPOSITION_BUG:
+		printk(KERN_ERR "sctp bug in state %d, "
+		       "event_type %d, event_id %d\n",
+		       state, event_type, subtype.chunk);
+		BUG();
+		break;
+
+	default:
+		printk(KERN_ERR "sctp impossible disposition %d "
+		       "in state %d, event_type %d, event_id %d\n",
+		       status, state, event_type, subtype.chunk);
+		BUG();
+		break;
+	};
+
+bail:
+	return error;
+}
+
+/********************************************************************
+ * 2nd Level Abstractions
+ ********************************************************************/
+
+/* This is the side-effect interpreter.  */
+int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
+			 sctp_state_t state, struct sctp_endpoint *ep,
+			 struct sctp_association *asoc, void *event_arg,
+			 sctp_disposition_t status, sctp_cmd_seq_t *commands,
+			 int gfp)
+{
+	int error = 0;
+	int force;
+	sctp_cmd_t *cmd;
+	sctp_chunk_t *new_obj;
+	sctp_chunk_t *chunk = NULL;
+	struct sctp_packet *packet;
+	struct list_head *pos;
+	struct timer_list *timer;
+	unsigned long timeout;
+	struct sctp_transport *t;
+	sctp_sackhdr_t sackh;
+
+	if(SCTP_EVENT_T_TIMEOUT != event_type)
+		chunk = (sctp_chunk_t *) event_arg;
+
+	/* Note:  This whole file is a huge candidate for rework.
+	 * For example, each command could either have its own handler, so
+	 * the loop would look like:
+	 *     while (cmds)
+	 *         cmd->handle(x, y, z)
+	 * --jgrimm
+	 */
+	while (NULL != (cmd = sctp_next_cmd(commands))) {
+		switch (cmd->verb) {
+		case SCTP_CMD_NOP:
+			/* Do nothing. */
+			break;
+
+		case SCTP_CMD_NEW_ASOC:
+			/* Register a new association.  */
+			asoc = cmd->obj.ptr;
+			/* Register with the endpoint.  */
+			sctp_endpoint_add_asoc(ep, asoc);
+			sctp_hash_established(asoc);
+			break;
+
+		case SCTP_CMD_UPDATE_ASSOC:
+		       sctp_assoc_update(asoc, cmd->obj.ptr);
+		       break;
+
+		case SCTP_CMD_PURGE_OUTQUEUE:
+		       sctp_outq_teardown(&asoc->outqueue);
+		       break;
+
+		case SCTP_CMD_DELETE_TCB:
+			/* Delete the current association.  */
+			sctp_cmd_delete_tcb(commands, asoc);
+			asoc = NULL;
+			break;
+
+		case SCTP_CMD_NEW_STATE:
+			/* Enter a new state.  */
+			sctp_cmd_new_state(commands, asoc, cmd->obj.state);
+			break;
+
+		case SCTP_CMD_REPORT_TSN:
+			/* Record the arrival of a TSN.  */
+			sctp_tsnmap_mark(&asoc->peer.tsn_map, cmd->obj.u32);
+			break;
+
+		case SCTP_CMD_GEN_SACK:
+			/* Generate a Selective ACK.
+			 * The argument tells us whether to just count
+			 * the packet and MAYBE generate a SACK, or
+			 * force a SACK out.
+			 */
+			force = cmd->obj.i32;
+			error = sctp_gen_sack(asoc, force, commands);
+			break;
+
+		case SCTP_CMD_PROCESS_SACK:
+			/* Process an inbound SACK.  */
+			error = sctp_cmd_process_sack(commands, asoc,
+						      cmd->obj.ptr);
+			break;
+
+		case SCTP_CMD_GEN_INIT_ACK:
+			/* Generate an INIT ACK chunk.  */
+			new_obj = sctp_make_init_ack(asoc, chunk, GFP_ATOMIC,
+						     0);
+			if (!new_obj)
+				goto nomem;
+
+			sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
+					SCTP_CHUNK(new_obj));
+			break;
+
+		case SCTP_CMD_PEER_INIT:
+			/* Process a unified INIT from the peer.
+			 * Note: Only used during INIT-ACK processing.  If
+			 * there is an error just return to the outter
+			 * layer which will bail.
+			 */
+			error = sctp_cmd_process_init(commands, asoc, chunk,
+						      cmd->obj.ptr, gfp);
+			break;
+
+		case SCTP_CMD_GEN_COOKIE_ECHO:
+			/* Generate a COOKIE ECHO chunk.  */
+			new_obj = sctp_make_cookie_echo(asoc, chunk);
+			if (!new_obj) {
+				if (cmd->obj.ptr)
+					sctp_free_chunk(cmd->obj.ptr);
+				goto nomem;
+			}
+			sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
+					SCTP_CHUNK(new_obj));
+
+			/* If there is an ERROR chunk to be sent along with
+			 * the COOKIE_ECHO, send it, too.
+			 */
+			if (cmd->obj.ptr)
+				sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
+						SCTP_CHUNK(cmd->obj.ptr));
+			break;
+
+		case SCTP_CMD_GEN_SHUTDOWN:
+			/* Generate SHUTDOWN when in SHUTDOWN_SENT state.
+			 * Reset error counts.
+			 */
+			asoc->overall_error_count = 0;
+
+			/* Generate a SHUTDOWN chunk.  */
+			new_obj = sctp_make_shutdown(asoc);
+			if (!new_obj)
+				goto nomem;
+			sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
+					SCTP_CHUNK(new_obj));
+			break;
+
+		case SCTP_CMD_CHUNK_ULP:
+			/* Send a chunk to the sockets layer.  */
+			SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n",
+					  "chunk_up:", cmd->obj.ptr,
+					  "ulpq:", &asoc->ulpq);
+			sctp_ulpq_tail_data(&asoc->ulpq, cmd->obj.ptr,
+					    GFP_ATOMIC);
+			break;
+
+		case SCTP_CMD_EVENT_ULP:
+			/* Send a notification to the sockets layer.  */
+			SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n",
+					  "event_up:",cmd->obj.ptr,
+					  "ulpq:",&asoc->ulpq);
+			sctp_ulpq_tail_event(&asoc->ulpq, cmd->obj.ptr);
+			break;
+
+		case SCTP_CMD_REPLY:
+			/* Send a chunk to our peer.  */
+			error = sctp_outq_tail(&asoc->outqueue,
+					       cmd->obj.ptr);
+			break;
+
+		case SCTP_CMD_SEND_PKT:
+			/* Send a full packet to our peer.  */
+			packet = cmd->obj.ptr;
+			sctp_packet_transmit(packet);
+			sctp_ootb_pkt_free(packet);
+			break;
+
+		case SCTP_CMD_RETRAN:
+			/* Mark a transport for retransmission.  */
+			sctp_retransmit(&asoc->outqueue, cmd->obj.transport,
+					SCTP_RTXR_T3_RTX);
+			break;
+
+		case SCTP_CMD_TRANSMIT:
+			/* Kick start transmission. */
+			error = sctp_outq_flush(&asoc->outqueue, 0);
+			break;
+
+		case SCTP_CMD_ECN_CE:
+			/* Do delayed CE processing.   */
+			sctp_do_ecn_ce_work(asoc, cmd->obj.u32);
+			break;
+
+		case SCTP_CMD_ECN_ECNE:
+			/* Do delayed ECNE processing. */
+			new_obj = sctp_do_ecn_ecne_work(asoc, cmd->obj.u32,
+							chunk);
+			if (new_obj)
+				sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
+						SCTP_CHUNK(new_obj));
+			break;
+
+		case SCTP_CMD_ECN_CWR:
+			/* Do delayed CWR processing.  */
+			sctp_do_ecn_cwr_work(asoc, cmd->obj.u32);
+			break;
+
+		case SCTP_CMD_SETUP_T2:
+			sctp_cmd_setup_t2(commands, asoc, cmd->obj.ptr);
+			break;
+
+		case SCTP_CMD_TIMER_START:
+			timer = &asoc->timers[cmd->obj.to];
+			timeout = asoc->timeouts[cmd->obj.to];
+			if (!timeout)
+				BUG();
+
+			timer->expires = jiffies + timeout;
+			sctp_association_hold(asoc);
+			add_timer(timer);
+			break;
+
+		case SCTP_CMD_TIMER_RESTART:
+			timer = &asoc->timers[cmd->obj.to];
+			timeout = asoc->timeouts[cmd->obj.to];
+			if (!mod_timer(timer, jiffies + timeout))
+				sctp_association_hold(asoc);
+			break;
+
+		case SCTP_CMD_TIMER_STOP:
+			timer = &asoc->timers[cmd->obj.to];
+			if (timer_pending(timer) && del_timer(timer))
+				sctp_association_put(asoc);
+			break;
+
+		case SCTP_CMD_INIT_RESTART:
+			/* Do the needed accounting and updates
+			 * associated with restarting an initialization
+			 * timer.
+			 */
+			asoc->counters[SCTP_COUNTER_INIT_ERROR]++;
+			asoc->timeouts[cmd->obj.to] *= 2;
+			if (asoc->timeouts[cmd->obj.to] >
+			    asoc->max_init_timeo) {
+				asoc->timeouts[cmd->obj.to] =
+					asoc->max_init_timeo;
+			}
+
+			/* If we've sent any data bundled with
+			 * COOKIE-ECHO we need to resend.
+			 */
+			list_for_each(pos, &asoc->peer.transport_addr_list) {
+				t = list_entry(pos, struct sctp_transport,
+					       transports);
+				sctp_retransmit_mark(&asoc->outqueue, t, 0);
+			}
+
+			sctp_add_cmd_sf(commands,
+					SCTP_CMD_TIMER_RESTART,
+					SCTP_TO(cmd->obj.to));
+			break;
+
+		case SCTP_CMD_INIT_FAILED:
+			sctp_cmd_init_failed(commands, asoc, cmd->obj.u32);
+			break;
+
+		case SCTP_CMD_ASSOC_FAILED:
+			sctp_cmd_assoc_failed(commands, asoc, event_type,
+					      subtype, chunk, cmd->obj.u32);
+			break;
+
+		case SCTP_CMD_COUNTER_INC:
+			asoc->counters[cmd->obj.counter]++;
+			break;
+
+		case SCTP_CMD_COUNTER_RESET:
+			asoc->counters[cmd->obj.counter] = 0;
+			break;
+
+		case SCTP_CMD_REPORT_DUP:
+			sctp_tsnmap_mark_dup(&asoc->peer.tsn_map,
+					     cmd->obj.u32);
+			break;
+
+		case SCTP_CMD_REPORT_BAD_TAG:
+			SCTP_DEBUG_PRINTK("vtag mismatch!\n");
+			break;
+
+		case SCTP_CMD_STRIKE:
+			/* Mark one strike against a transport.  */
+			sctp_do_8_2_transport_strike(asoc, cmd->obj.transport);
+			break;
+
+		case SCTP_CMD_TRANSPORT_RESET:
+			t = cmd->obj.transport;
+			sctp_cmd_transport_reset(commands, asoc, t);
+			break;
+
+		case SCTP_CMD_TRANSPORT_ON:
+			t = cmd->obj.transport;
+			sctp_cmd_transport_on(commands, asoc, t, chunk);
+			break;
+
+		case SCTP_CMD_HB_TIMERS_START:
+			sctp_cmd_hb_timers_start(commands, asoc);
+			break;
+
+		case SCTP_CMD_HB_TIMER_UPDATE:
+			t = cmd->obj.transport;
+			sctp_cmd_hb_timer_update(commands, asoc, t);
+			break;
+
+		case SCTP_CMD_HB_TIMERS_STOP:
+			sctp_cmd_hb_timers_stop(commands, asoc);
+			break;
+
+		case SCTP_CMD_REPORT_ERROR:
+			error = cmd->obj.error;
+			break;
+
+		case SCTP_CMD_PROCESS_CTSN:
+			/* Dummy up a SACK for processing. */
+			sackh.cum_tsn_ack = cmd->obj.u32;
+			sackh.a_rwnd = 0;
+			sackh.num_gap_ack_blocks = 0;
+			sackh.num_dup_tsns = 0;
+			sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_SACK,
+					SCTP_SACKH(&sackh));
+			break;
+
+		case SCTP_CMD_DISCARD_PACKET:
+			/* We need to discard the whole packet.  */
+			chunk->pdiscard = 1;
+			break;
+
+		case SCTP_CMD_RTO_PENDING:
+			t = cmd->obj.transport;
+			t->rto_pending = 1;
+			break;
+
+		case SCTP_CMD_PART_DELIVER:
+			sctp_ulpq_partial_delivery(&asoc->ulpq, cmd->obj.ptr,
+						   GFP_ATOMIC);
+			break;
+
+		case SCTP_CMD_RENEGE:
+			sctp_ulpq_renege(&asoc->ulpq, cmd->obj.ptr,
+					 GFP_ATOMIC);
+			break;
+
+		default:
+			printk(KERN_WARNING "Impossible command: %u, %p\n",
+			       cmd->verb, cmd->obj.ptr);
+			break;
+		};
+		if (error)
+			return error;
+	}
+
+	return error;
+
+nomem:
+	error = -ENOMEM;
+	return error;
+}
+
diff -urN linux-2.4.23-pre5/net/sctp/sm_statefuns.c linux-2.4.23-pre6/net/sctp/sm_statefuns.c
--- linux-2.4.23-pre5/net/sctp/sm_statefuns.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/sm_statefuns.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,4545 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001-2002 International Business Machines, Corp.
+ * Copyright (c) 2001-2002 Intel Corp.
+ * Copyright (c) 2002      Nokia Corp.
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * This is part of the SCTP Linux Kernel Reference Implementation.
+ *
+ * These are the state functions for the state machine.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Karl Knutson          <karl@athena.chicago.il.us>
+ *    Mathew Kotowsky       <kotowsky@sctp.org>
+ *    Sridhar Samudrala     <samudrala@us.ibm.com>
+ *    Jon Grimm             <jgrimm@us.ibm.com>
+ *    Hui Huang 	    <hui.huang@nokia.com>
+ *    Dajiang Zhang 	    <dajiang.zhang@nokia.com>
+ *    Daisy Chang	    <daisyc@us.ibm.com>
+ *    Ardelle Fan	    <ardelle.fan@intel.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <net/sock.h>
+#include <net/inet_ecn.h>
+#include <linux/skbuff.h>
+#include <net/sctp/sctp.h>
+#include <net/sctp/sm.h>
+#include <net/sctp/structs.h>
+
+/**********************************************************
+ * These are the state functions for handling chunk events.
+ **********************************************************/
+
+/*
+ * Process the final SHUTDOWN COMPLETE.
+ *
+ * Section: 4 (C) (diagram), 9.2
+ * Upon reception of the SHUTDOWN COMPLETE chunk the endpoint will verify
+ * that it is in SHUTDOWN-ACK-SENT state, if it is not the chunk should be
+ * discarded. If the endpoint is in the SHUTDOWN-ACK-SENT state the endpoint
+ * should stop the T2-shutdown timer and remove all knowledge of the
+ * association (and thus the association enters the CLOSED state).
+ *
+ * Verification Tag: 8.5.1(C)
+ * C) Rules for packet carrying SHUTDOWN COMPLETE:
+ * ...
+ * - The receiver of a SHUTDOWN COMPLETE shall accept the packet if the
+ *   Verification Tag field of the packet matches its own tag OR it is
+ *   set to its peer's tag and the T bit is set in the Chunk Flags.
+ *   Otherwise, the receiver MUST silently discard the packet and take
+ *   no further action. An endpoint MUST ignore the SHUTDOWN COMPLETE if
+ *   it is not in the SHUTDOWN-ACK-SENT state.
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_do_4_C(const struct sctp_endpoint *ep,
+				  const struct sctp_association *asoc,
+				  const sctp_subtype_t type,
+				  void *arg,
+				  sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+	struct sctp_ulpevent *ev;
+
+	/* RFC 2960 6.10 Bundling
+	 *
+	 * An endpoint MUST NOT bundle INIT, INIT ACK or
+	 * SHUTDOWN COMPLETE with any other chunks.
+	 */
+	if (!chunk->singleton)
+		return SCTP_DISPOSITION_VIOLATION;
+
+	if (!sctp_vtag_verify_either(chunk, asoc))
+		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+
+	/* RFC 2960 10.2 SCTP-to-ULP
+	 *
+	 * H) SHUTDOWN COMPLETE notification
+	 *
+	 * When SCTP completes the shutdown procedures (section 9.2) this
+	 * notification is passed to the upper layer.
+	 */
+	ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_SHUTDOWN_COMP,
+					     0, 0, 0, GFP_ATOMIC);
+	if (!ev)
+		goto nomem;
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev));
+
+	/* Upon reception of the SHUTDOWN COMPLETE chunk the endpoint
+	 * will verify that it is in SHUTDOWN-ACK-SENT state, if it is
+	 * not the chunk should be discarded. If the endpoint is in
+	 * the SHUTDOWN-ACK-SENT state the endpoint should stop the
+	 * T2-shutdown timer and remove all knowledge of the
+	 * association (and thus the association enters the CLOSED
+	 * state).
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN));
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD));
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+			SCTP_STATE(SCTP_STATE_CLOSED));
+
+	SCTP_INC_STATS(SctpShutdowns);
+	SCTP_DEC_STATS(SctpCurrEstab);
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
+
+	return SCTP_DISPOSITION_DELETE_TCB;
+
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/*
+ * Respond to a normal INIT chunk.
+ * We are the side that is being asked for an association.
+ *
+ * Section: 5.1 Normal Establishment of an Association, B
+ * B) "Z" shall respond immediately with an INIT ACK chunk.  The
+ *    destination IP address of the INIT ACK MUST be set to the source
+ *    IP address of the INIT to which this INIT ACK is responding.  In
+ *    the response, besides filling in other parameters, "Z" must set the
+ *    Verification Tag field to Tag_A, and also provide its own
+ *    Verification Tag (Tag_Z) in the Initiate Tag field.
+ *
+ * Verification Tag: No checking.
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					const sctp_subtype_t type,
+					void *arg,
+					sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+	sctp_chunk_t *repl;
+	struct sctp_association *new_asoc;
+	sctp_chunk_t *err_chunk;
+	struct sctp_packet *packet;
+	sctp_unrecognized_param_t *unk_param;
+	struct sock *sk;
+	int len;
+
+	/* 6.10 Bundling
+	 * An endpoint MUST NOT bundle INIT, INIT ACK or
+	 * SHUTDOWN COMPLETE with any other chunks.
+	 */
+	if (!chunk->singleton)
+		return SCTP_DISPOSITION_VIOLATION;
+
+	/* If the packet is an OOTB packet which is temporarily on the
+	 * control endpoint, respond with an ABORT.
+	 */
+	if (ep == sctp_sk((sctp_get_ctl_sock()))->ep)
+		return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands);
+
+	sk = ep->base.sk;
+	/* If the endpoint is not listening or if the number of associations
+	 * on the TCP-style socket exceed the max backlog, respond with an
+	 * ABORT.
+	 */
+	if ((SCTP_SS_LISTENING != sk->state) ||
+	    ((SCTP_SOCKET_TCP == sctp_sk(sk)->type) &&
+	     (sk->ack_backlog >= sk->max_ack_backlog)))
+		return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands);
+
+	/* Verify the INIT chunk before processing it. */
+	err_chunk = NULL;
+	if (!sctp_verify_init(asoc, chunk->chunk_hdr->type,
+			      (sctp_init_chunk_t *)chunk->chunk_hdr, chunk,
+			      &err_chunk)) {
+		/* This chunk contains fatal error. It is to be discarded.
+		 * Send an ABORT, with causes if there is any.
+		 */
+		if (err_chunk) {
+			packet = sctp_abort_pkt_new(ep, asoc, arg,
+					(__u8 *)(err_chunk->chunk_hdr) +
+					sizeof(sctp_chunkhdr_t),
+					ntohs(err_chunk->chunk_hdr->length) -
+					sizeof(sctp_chunkhdr_t));
+
+			sctp_free_chunk(err_chunk);
+
+			if (packet) {
+				sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
+						SCTP_PACKET(packet));
+				SCTP_INC_STATS(SctpOutCtrlChunks);
+				return SCTP_DISPOSITION_CONSUME;
+			} else {
+				return SCTP_DISPOSITION_NOMEM;
+			}
+		} else {
+			return sctp_sf_tabort_8_4_8(ep, asoc, type, arg,
+						    commands);
+		}
+	}
+
+        /* Grab the INIT header.  */
+	chunk->subh.init_hdr = (sctp_inithdr_t *)chunk->skb->data;
+
+	/* Tag the variable length parameters.  */
+	chunk->param_hdr.v = skb_pull(chunk->skb, sizeof(sctp_inithdr_t));
+
+	new_asoc = sctp_make_temp_asoc(ep, chunk, GFP_ATOMIC);
+	if (!new_asoc)
+		goto nomem;
+
+	/* The call, sctp_process_init(), can fail on memory allocation.  */
+	if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
+			       sctp_source(chunk),
+			       (sctp_init_chunk_t *)chunk->chunk_hdr,
+			       GFP_ATOMIC))
+		goto nomem_init;
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc));
+
+	/* B) "Z" shall respond immediately with an INIT ACK chunk.  */
+
+	/* If there are errors need to be reported for unknown parameters,
+	 * make sure to reserve enough room in the INIT ACK for them.
+	 */
+	len = 0;
+	if (err_chunk)
+		len = ntohs(err_chunk->chunk_hdr->length) -
+			sizeof(sctp_chunkhdr_t);
+
+	if (sctp_assoc_set_bind_addr_from_ep(new_asoc, GFP_ATOMIC) < 0)
+		goto nomem_ack;
+
+	repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC, len);
+	if (!repl)
+		goto nomem_ack;
+
+	/* If there are errors need to be reported for unknown parameters,
+	 * include them in the outgoing INIT ACK as "Unrecognized parameter"
+	 * parameter.
+	 */
+	if (err_chunk) {
+		/* Get the "Unrecognized parameter" parameter(s) out of the
+		 * ERROR chunk generated by sctp_verify_init(). Since the
+		 * error cause code for "unknown parameter" and the
+		 * "Unrecognized parameter" type is the same, we can
+		 * construct the parameters in INIT ACK by copying the
+		 * ERROR causes over.
+		 */
+		unk_param = (sctp_unrecognized_param_t *)
+			    ((__u8 *)(err_chunk->chunk_hdr) +
+			    sizeof(sctp_chunkhdr_t));
+		/* Replace the cause code with the "Unrecognized parameter"
+		 * parameter type.
+		 */
+		sctp_addto_chunk(repl, len, unk_param);
+		sctp_free_chunk(err_chunk);
+	}
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
+
+	/*
+	 * Note:  After sending out INIT ACK with the State Cookie parameter,
+	 * "Z" MUST NOT allocate any resources, nor keep any states for the
+	 * new association.  Otherwise, "Z" will be vulnerable to resource
+	 * attacks.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
+
+	return SCTP_DISPOSITION_DELETE_TCB;
+
+nomem_ack:
+	if (err_chunk)
+		sctp_free_chunk(err_chunk);
+nomem_init:
+	sctp_association_free(new_asoc);
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/*
+ * Respond to a normal INIT ACK chunk.
+ * We are the side that is initiating the association.
+ *
+ * Section: 5.1 Normal Establishment of an Association, C
+ * C) Upon reception of the INIT ACK from "Z", "A" shall stop the T1-init
+ *    timer and leave COOKIE-WAIT state. "A" shall then send the State
+ *    Cookie received in the INIT ACK chunk in a COOKIE ECHO chunk, start
+ *    the T1-cookie timer, and enter the COOKIE-ECHOED state.
+ *
+ *    Note: The COOKIE ECHO chunk can be bundled with any pending outbound
+ *    DATA chunks, but it MUST be the first chunk in the packet and
+ *    until the COOKIE ACK is returned the sender MUST NOT send any
+ *    other packets to the peer.
+ *
+ * Verification Tag: 3.3.3
+ *   If the value of the Initiate Tag in a received INIT ACK chunk is
+ *   found to be 0, the receiver MUST treat it as an error and close the
+ *   association by transmitting an ABORT.
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
+				       const struct sctp_association *asoc,
+				       const sctp_subtype_t type,
+				       void *arg,
+				       sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+	sctp_init_chunk_t *initchunk;
+	__u32 init_tag;
+	sctp_chunk_t *err_chunk;
+	struct sctp_packet *packet;
+	sctp_disposition_t ret;
+
+	/* 6.10 Bundling
+	 * An endpoint MUST NOT bundle INIT, INIT ACK or
+	 * SHUTDOWN COMPLETE with any other chunks.
+	 */
+	if (!chunk->singleton)
+		return SCTP_DISPOSITION_VIOLATION;
+
+	/* Grab the INIT header.  */
+	chunk->subh.init_hdr = (sctp_inithdr_t *) chunk->skb->data;
+
+	init_tag = ntohl(chunk->subh.init_hdr->init_tag);
+
+	/* Verification Tag: 3.3.3
+	 *   If the value of the Initiate Tag in a received INIT ACK
+	 *   chunk is found to be 0, the receiver MUST treat it as an
+	 *   error and close the association by transmitting an ABORT.
+	 */
+	if (!init_tag) {
+		sctp_chunk_t *reply = sctp_make_abort(asoc, chunk, 0);
+		if (!reply)
+			goto nomem;
+
+		sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
+		sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+				SCTP_STATE(SCTP_STATE_CLOSED));
+		SCTP_INC_STATS(SctpAborteds);
+		sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
+		return SCTP_DISPOSITION_DELETE_TCB;
+	}
+
+	/* Verify the INIT chunk before processing it. */
+	err_chunk = NULL;
+	if (!sctp_verify_init(asoc, chunk->chunk_hdr->type,
+			      (sctp_init_chunk_t *)chunk->chunk_hdr, chunk,
+			      &err_chunk)) {
+
+		SCTP_INC_STATS(SctpAborteds);
+
+		/* This chunk contains fatal error. It is to be discarded.
+		 * Send an ABORT, with causes if there is any.
+		 */
+		if (err_chunk) {
+			packet = sctp_abort_pkt_new(ep, asoc, arg,
+					(__u8 *)(err_chunk->chunk_hdr) +
+					sizeof(sctp_chunkhdr_t),
+					ntohs(err_chunk->chunk_hdr->length) -
+					sizeof(sctp_chunkhdr_t));
+
+			sctp_free_chunk(err_chunk);
+
+			if (packet) {
+				sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
+						SCTP_PACKET(packet));
+				SCTP_INC_STATS(SctpOutCtrlChunks);
+				sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+						SCTP_STATE(SCTP_STATE_CLOSED));
+				sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
+						SCTP_NULL());
+				return SCTP_DISPOSITION_CONSUME;
+			} else {
+				sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+						SCTP_STATE(SCTP_STATE_CLOSED));
+				sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
+						SCTP_NULL());
+				return SCTP_DISPOSITION_NOMEM;
+			}
+		} else {
+			ret = sctp_sf_tabort_8_4_8(ep, asoc, type, arg,
+						   commands);
+			sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+					SCTP_STATE(SCTP_STATE_CLOSED));
+			sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
+					SCTP_NULL());
+			return ret;
+		}
+	}
+
+	/* Tag the variable length parameters.  Note that we never
+	 * convert the parameters in an INIT chunk.
+	 */
+	chunk->param_hdr.v = skb_pull(chunk->skb, sizeof(sctp_inithdr_t));
+
+	initchunk = (sctp_init_chunk_t *) chunk->chunk_hdr;
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_PEER_INIT,
+			SCTP_PEER_INIT(initchunk));
+
+	/* 5.1 C) "A" shall stop the T1-init timer and leave
+	 * COOKIE-WAIT state.  "A" shall then ... start the T1-cookie
+	 * timer, and enter the COOKIE-ECHOED state.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
+	sctp_add_cmd_sf(commands, SCTP_CMD_COUNTER_RESET,
+			SCTP_COUNTER(SCTP_COUNTER_INIT_ERROR));
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE));
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+			SCTP_STATE(SCTP_STATE_COOKIE_ECHOED));
+
+	/* 5.1 C) "A" shall then send the State Cookie received in the
+	 * INIT ACK chunk in a COOKIE ECHO chunk, ...
+	 */
+	/* If there is any errors to report, send the ERROR chunk generated
+	 * for unknown parameters as well.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_GEN_COOKIE_ECHO,
+			SCTP_CHUNK(err_chunk));
+
+	return SCTP_DISPOSITION_CONSUME;
+
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/*
+ * Respond to a normal COOKIE ECHO chunk.
+ * We are the side that is being asked for an association.
+ *
+ * Section: 5.1 Normal Establishment of an Association, D
+ * D) Upon reception of the COOKIE ECHO chunk, Endpoint "Z" will reply
+ *    with a COOKIE ACK chunk after building a TCB and moving to
+ *    the ESTABLISHED state. A COOKIE ACK chunk may be bundled with
+ *    any pending DATA chunks (and/or SACK chunks), but the COOKIE ACK
+ *    chunk MUST be the first chunk in the packet.
+ *
+ *   IMPLEMENTATION NOTE: An implementation may choose to send the
+ *   Communication Up notification to the SCTP user upon reception
+ *   of a valid COOKIE ECHO chunk.
+ *
+ * Verification Tag: 8.5.1 Exceptions in Verification Tag Rules
+ * D) Rules for packet carrying a COOKIE ECHO
+ *
+ * - When sending a COOKIE ECHO, the endpoint MUST use the value of the
+ *   Initial Tag received in the INIT ACK.
+ *
+ * - The receiver of a COOKIE ECHO follows the procedures in Section 5.
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep,
+				      const struct sctp_association *asoc,
+				      const sctp_subtype_t type, void *arg,
+				      sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+	struct sctp_association *new_asoc;
+	sctp_init_chunk_t *peer_init;
+	sctp_chunk_t *repl;
+	struct sctp_ulpevent *ev;
+	int error = 0;
+	sctp_chunk_t *err_chk_p;
+
+	/* If the packet is an OOTB packet which is temporarily on the
+	 * control endpoint, respond with an ABORT.
+	 */
+	if (ep == sctp_sk((sctp_get_ctl_sock()))->ep)
+		return sctp_sf_ootb(ep, asoc, type, arg, commands);
+
+	/* "Decode" the chunk.  We have no optional parameters so we
+	 * are in good shape.
+	 */
+        chunk->subh.cookie_hdr =
+		(sctp_signed_cookie_t *)chunk->skb->data;
+	skb_pull(chunk->skb,
+		 ntohs(chunk->chunk_hdr->length) - sizeof(sctp_chunkhdr_t));
+
+	/* 5.1 D) Upon reception of the COOKIE ECHO chunk, Endpoint
+	 * "Z" will reply with a COOKIE ACK chunk after building a TCB
+	 * and moving to the ESTABLISHED state.
+	 */
+	new_asoc = sctp_unpack_cookie(ep, asoc, chunk, GFP_ATOMIC, &error,
+				      &err_chk_p);
+
+	/* FIXME:
+	 * If the re-build failed, what is the proper error path
+	 * from here?
+	 *
+	 * [We should abort the association. --piggy]
+	 */
+	if (!new_asoc) {
+		/* FIXME: Several errors are possible.  A bad cookie should
+		 * be silently discarded, but think about logging it too.
+		 */
+		switch (error) {
+		case -SCTP_IERROR_NOMEM:
+			goto nomem;
+
+		case -SCTP_IERROR_STALE_COOKIE:
+			sctp_send_stale_cookie_err(ep, asoc, chunk, commands,
+						   err_chk_p);
+			return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+
+		case -SCTP_IERROR_BAD_SIG:
+		default:
+			return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+		};
+	}
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc));
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+			SCTP_STATE(SCTP_STATE_ESTABLISHED));
+	SCTP_INC_STATS(SctpCurrEstab);
+	SCTP_INC_STATS(SctpPassiveEstabs);
+	sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, SCTP_NULL());
+
+	if (new_asoc->autoclose)
+		sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START,
+				SCTP_TO(SCTP_EVENT_TIMEOUT_AUTOCLOSE));
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_TRANSMIT, SCTP_NULL());
+
+	/* Re-build the bind address for the association is done in
+	 * the sctp_unpack_cookie() already.
+	 */
+	/* This is a brand-new association, so these are not yet side
+	 * effects--it is safe to run them here.
+	 */
+	peer_init = &chunk->subh.cookie_hdr->c.peer_init[0];
+
+	if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
+			       &chunk->subh.cookie_hdr->c.peer_addr,
+			       peer_init, GFP_ATOMIC))
+		goto nomem_init;
+
+	repl = sctp_make_cookie_ack(new_asoc, chunk);
+	if (!repl)
+		goto nomem_repl;
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
+
+	/* RFC 2960 5.1 Normal Establishment of an Association
+	 *
+	 * D) IMPLEMENTATION NOTE: An implementation may choose to
+	 * send the Communication Up notification to the SCTP user
+	 * upon reception of a valid COOKIE ECHO chunk.
+	 */
+	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);
+	if (!ev)
+		goto nomem_ev;
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev));
+
+	return SCTP_DISPOSITION_CONSUME;
+
+nomem_ev:
+	sctp_free_chunk(repl);
+nomem_repl:
+nomem_init:
+	sctp_association_free(new_asoc);
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/*
+ * Respond to a normal COOKIE ACK chunk.
+ * We are the side that is being asked for an association.
+ *
+ * RFC 2960 5.1 Normal Establishment of an Association
+ *
+ * E) Upon reception of the COOKIE ACK, endpoint "A" will move from the
+ *    COOKIE-ECHOED state to the ESTABLISHED state, stopping the T1-cookie
+ *    timer. It may also notify its ULP about the successful
+ *    establishment of the association with a Communication Up
+ *    notification (see Section 10).
+ *
+ * Verification Tag:
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_do_5_1E_ca(const struct sctp_endpoint *ep,
+				      const struct sctp_association *asoc,
+				      const sctp_subtype_t type, void *arg,
+				      sctp_cmd_seq_t *commands)
+{
+	struct sctp_ulpevent *ev;
+
+	/* RFC 2960 5.1 Normal Establishment of an Association
+	 *
+	 * E) Upon reception of the COOKIE ACK, endpoint "A" will move
+	 * from the COOKIE-ECHOED state to the ESTABLISHED state,
+	 * stopping the T1-cookie timer.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE));
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+			SCTP_STATE(SCTP_STATE_ESTABLISHED));
+	SCTP_INC_STATS(SctpCurrEstab);
+	SCTP_INC_STATS(SctpActiveEstabs);
+	sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, SCTP_NULL());
+	if (asoc->autoclose)
+		sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START,
+				SCTP_TO(SCTP_EVENT_TIMEOUT_AUTOCLOSE));
+	sctp_add_cmd_sf(commands, SCTP_CMD_TRANSMIT, SCTP_NULL());
+
+	/* It may also notify its ULP about the successful
+	 * establishment of the association with a Communication Up
+	 * notification (see Section 10).
+	 */
+	ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_UP,
+					     0, asoc->c.sinit_num_ostreams,
+					     asoc->c.sinit_max_instreams,
+					     GFP_ATOMIC);
+
+	if (!ev)
+		goto nomem;
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev));
+
+	return SCTP_DISPOSITION_CONSUME;
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/* Generate and sendout a heartbeat packet.  */
+sctp_disposition_t sctp_sf_heartbeat(const struct sctp_endpoint *ep,
+				     const struct sctp_association *asoc,
+				     const sctp_subtype_t type,
+				     void *arg,
+				     sctp_cmd_seq_t *commands)
+{
+	struct sctp_transport *transport = (struct sctp_transport *) arg;
+	sctp_chunk_t *reply;
+	sctp_sender_hb_info_t hbinfo;
+	size_t paylen = 0;
+
+	hbinfo.param_hdr.type = SCTP_PARAM_HEARTBEAT_INFO;
+	hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t));
+	hbinfo.daddr = transport->ipaddr;
+	hbinfo.sent_at = jiffies;
+
+	/* Send a heartbeat to our peer.  */
+	paylen = sizeof(sctp_sender_hb_info_t);
+	reply = sctp_make_heartbeat(asoc, transport, &hbinfo, paylen);
+	if (!reply)
+		return SCTP_DISPOSITION_NOMEM;
+
+	/* Set rto_pending indicating that an RTT measurement
+	 * is started with this heartbeat chunk.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_RTO_PENDING,
+			SCTP_TRANSPORT(transport));
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
+	return SCTP_DISPOSITION_CONSUME;
+}
+
+/* Generate a HEARTBEAT packet on the given transport.  */
+sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					const sctp_subtype_t type,
+					void *arg,
+					sctp_cmd_seq_t *commands)
+{
+	struct sctp_transport *transport = (struct sctp_transport *) arg;
+
+	if (asoc->overall_error_count >= asoc->overall_error_threshold) {
+		/* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
+		sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
+				SCTP_U32(SCTP_ERROR_NO_ERROR));
+		SCTP_INC_STATS(SctpAborteds);
+		SCTP_DEC_STATS(SctpCurrEstab);
+		return SCTP_DISPOSITION_DELETE_TCB;
+	}
+
+	/* Section 3.3.5.
+	 * The Sender-specific Heartbeat Info field should normally include
+	 * information about the sender's current time when this HEARTBEAT
+	 * chunk is sent and the destination transport address to which this
+	 * HEARTBEAT is sent (see Section 8.3).
+	 */
+
+	if (transport->hb_allowed) {
+		if (SCTP_DISPOSITION_NOMEM ==
+				sctp_sf_heartbeat(ep, asoc, type, arg,
+						  commands))
+			return SCTP_DISPOSITION_NOMEM;
+		/* Set transport error counter and association error counter
+		 * when sending heartbeat.
+		 */
+		sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_RESET,
+				SCTP_TRANSPORT(transport));
+	}
+	sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMER_UPDATE,
+			SCTP_TRANSPORT(transport));
+
+        return SCTP_DISPOSITION_CONSUME;
+}
+
+/*
+ * Process an heartbeat request.
+ *
+ * Section: 8.3 Path Heartbeat
+ * The receiver of the HEARTBEAT should immediately respond with a
+ * HEARTBEAT ACK that contains the Heartbeat Information field copied
+ * from the received HEARTBEAT chunk.
+ *
+ * Verification Tag:  8.5 Verification Tag [Normal verification]
+ * When receiving an SCTP packet, the endpoint MUST ensure that the
+ * value in the Verification Tag field of the received SCTP packet
+ * matches its own Tag. If the received Verification Tag value does not
+ * match the receiver's own tag value, the receiver shall silently
+ * discard the packet and shall not process it any further except for
+ * those cases listed in Section 8.5.1 below.
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_beat_8_3(const struct sctp_endpoint *ep,
+				    const struct sctp_association *asoc,
+				    const sctp_subtype_t type,
+				    void *arg,
+				    sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+	sctp_chunk_t *reply;
+	size_t paylen = 0;
+
+	/* 8.5 When receiving an SCTP packet, the endpoint MUST ensure
+	 * that the value in the Verification Tag field of the
+	 * received SCTP packet matches its own Tag. If the received
+	 * Verification Tag value does not match the receiver's own
+	 * tag value, the receiver shall silently discard the packet...
+	 */
+	if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag)
+		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+
+	/* 8.3 The receiver of the HEARTBEAT should immediately
+	 * respond with a HEARTBEAT ACK that contains the Heartbeat
+	 * Information field copied from the received HEARTBEAT chunk.
+	 */
+	chunk->subh.hb_hdr = (sctp_heartbeathdr_t *) chunk->skb->data;
+	paylen = ntohs(chunk->chunk_hdr->length) - sizeof(sctp_chunkhdr_t);
+	skb_pull(chunk->skb, paylen);
+
+	reply = sctp_make_heartbeat_ack(asoc, chunk,
+					chunk->subh.hb_hdr, paylen);
+	if (!reply)
+		goto nomem;
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
+	return SCTP_DISPOSITION_CONSUME;
+
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/*
+ * Process the returning HEARTBEAT ACK.
+ *
+ * Section: 8.3 Path Heartbeat
+ * Upon the receipt of the HEARTBEAT ACK, the sender of the HEARTBEAT
+ * should clear the error counter of the destination transport
+ * address to which the HEARTBEAT was sent, and mark the destination
+ * transport address as active if it is not so marked. The endpoint may
+ * optionally report to the upper layer when an inactive destination
+ * address is marked as active due to the reception of the latest
+ * HEARTBEAT ACK. The receiver of the HEARTBEAT ACK must also
+ * clear the association overall error count as well (as defined
+ * in section 8.1).
+ *
+ * The receiver of the HEARTBEAT ACK should also perform an RTT
+ * measurement for that destination transport address using the time
+ * value carried in the HEARTBEAT ACK chunk.
+ *
+ * Verification Tag:  8.5 Verification Tag [Normal verification]
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					const sctp_subtype_t type,
+					void *arg,
+					sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+	union sctp_addr from_addr;
+	struct sctp_transport *link;
+	sctp_sender_hb_info_t *hbinfo;
+	unsigned long max_interval;
+
+	/* 8.5 When receiving an SCTP packet, the endpoint MUST ensure
+	 * that the value in the Verification Tag field of the
+	 * received SCTP packet matches its own Tag. ...
+	 */
+	if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag)
+		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+
+	hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data;
+	from_addr = hbinfo->daddr;
+	link = sctp_assoc_lookup_paddr(asoc, &from_addr);
+
+	/* This should never happen, but lets log it if so.  */
+	if (!link) {
+		printk(KERN_WARNING
+		       "%s: Could not find address %d.%d.%d.%d\n",
+		       __FUNCTION__, NIPQUAD(from_addr.v4.sin_addr));
+		return SCTP_DISPOSITION_DISCARD;
+	}
+
+	max_interval = link->hb_interval + link->rto;
+
+	/* Check if the timestamp looks valid.  */
+	if (time_after(hbinfo->sent_at, jiffies) ||
+	    time_after(jiffies, hbinfo->sent_at + max_interval)) {
+		SCTP_DEBUG_PRINTK("%s: HEARTBEAT ACK with invalid timestamp"
+				  "received for transport: %p\n",
+				   __FUNCTION__, link);
+		return SCTP_DISPOSITION_DISCARD;
+	}
+
+	/* 8.3 Upon the receipt of the HEARTBEAT ACK, the sender of
+	 * the HEARTBEAT should clear the error counter of the
+	 * destination transport address to which the HEARTBEAT was
+	 * sent and mark the destination transport address as active if
+	 * it is not so marked.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_ON, SCTP_TRANSPORT(link));
+
+	return SCTP_DISPOSITION_CONSUME;
+}
+
+/* Helper function to send out an abort for the restart
+ * condition.
+ */
+static int sctp_sf_send_restart_abort(union sctp_addr *ssa,
+				      sctp_chunk_t *init,
+				      sctp_cmd_seq_t *commands)
+{
+	int len;
+	struct sctp_packet *pkt;
+	sctp_addr_param_t *addrparm;
+	sctp_errhdr_t *errhdr;
+	struct sctp_endpoint *ep;
+	char buffer[sizeof(sctp_errhdr_t) + sizeof(sctp_addr_param_t)];
+
+	/* Build the error on the stack.   We are way to malloc crazy
+	 * throughout the code today.
+	 */
+	errhdr = (sctp_errhdr_t *)buffer;
+	addrparm = (sctp_addr_param_t *)errhdr->variable;
+
+	/* Copy into a parm format. */
+	len = sockaddr2sctp_addr(ssa, addrparm);
+	len += sizeof(sctp_errhdr_t);
+
+	errhdr->cause = SCTP_ERROR_RESTART;
+	errhdr->length = htons(len);
+
+	/* Assign to the control socket. */
+	ep = sctp_sk((sctp_get_ctl_sock()))->ep;
+
+	/* Association is NULL since this may be a restart attack and we
+	 * want to send back the attacker's vtag.
+	 */
+	pkt = sctp_abort_pkt_new(ep, NULL, init, errhdr, len);
+
+	if (!pkt)
+		goto out;
+	sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(pkt));
+
+	SCTP_INC_STATS(SctpOutCtrlChunks);
+
+	/* Discard the rest of the inbound packet. */
+	sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET, SCTP_NULL());
+
+out:
+	/* Even if there is no memory, treat as a failure so
+	 * the packet will get dropped.
+	 */
+	return 0;
+}
+
+/* A restart is occurring, check to make sure no new addresses
+ * are being added as we may be under a takeover attack.
+ */
+static int sctp_sf_check_restart_addrs(const struct sctp_association *new_asoc,
+				       const struct sctp_association *asoc,
+				       sctp_chunk_t *init,
+				       sctp_cmd_seq_t *commands)
+{
+	struct sctp_transport *new_addr, *addr;
+	struct list_head *pos, *pos2;
+	int found;
+
+	/* Implementor's Guide - Sectin 5.2.2
+	 * ...
+	 * Before responding the endpoint MUST check to see if the
+	 * unexpected INIT adds new addresses to the association. If new
+	 * addresses are added to the association, the endpoint MUST respond
+	 * with an ABORT..
+	 */
+
+	/* Search through all current addresses and make sure
+	 * we aren't adding any new ones.
+	 */
+	new_addr = 0;
+	found = 0;
+
+	list_for_each(pos, &new_asoc->peer.transport_addr_list) {
+		new_addr = list_entry(pos, struct sctp_transport, transports);
+		found = 0;
+		list_for_each(pos2, &asoc->peer.transport_addr_list) {
+			addr = list_entry(pos2, struct sctp_transport,
+					  transports);
+			if (sctp_cmp_addr_exact(&new_addr->ipaddr,
+						&addr->ipaddr)) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found)
+			break;
+	}
+
+	/* If a new address was added, ABORT the sender. */
+	if (!found && new_addr) {
+		sctp_sf_send_restart_abort(&new_addr->ipaddr, init, commands);
+	}
+
+	/* Return success if all addresses were found. */
+	return found;
+}
+
+/* Populate the verification/tie tags based on overlapping INIT
+ * scenario.
+ *
+ * Note: Do not use in CLOSED or SHUTDOWN-ACK-SENT state.
+ */
+static void sctp_tietags_populate(struct sctp_association *new_asoc,
+				  const struct sctp_association *asoc)
+{
+	switch (asoc->state) {
+
+	/* 5.2.1 INIT received in COOKIE-WAIT or COOKIE-ECHOED State */
+
+	case SCTP_STATE_COOKIE_WAIT:
+		new_asoc->c.my_vtag     = asoc->c.my_vtag;
+		new_asoc->c.my_ttag     = asoc->c.my_vtag;
+		new_asoc->c.peer_ttag   = 0;
+		break;
+
+	case SCTP_STATE_COOKIE_ECHOED:
+		new_asoc->c.my_vtag     = asoc->c.my_vtag;
+		new_asoc->c.my_ttag     = asoc->c.my_vtag;
+		new_asoc->c.peer_ttag   = asoc->c.peer_vtag;
+		break;
+
+	/* 5.2.2 Unexpected INIT in States Other than CLOSED, COOKIE-ECHOED,
+	 * COOKIE-WAIT and SHUTDOWN-ACK-SENT
+	 */
+	default:
+		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
+	 * outbound streams) into the INIT ACK and cookie.
+	 */
+	new_asoc->rwnd                  = asoc->rwnd;
+	new_asoc->c.sinit_num_ostreams  = asoc->c.sinit_num_ostreams;
+	new_asoc->c.sinit_max_instreams = asoc->c.sinit_max_instreams;
+	new_asoc->c.initial_tsn         = asoc->c.initial_tsn;
+}
+
+/*
+ * Compare vtag/tietag values to determine unexpected COOKIE-ECHO
+ * handling action.
+ *
+ * RFC 2960 5.2.4 Handle a COOKIE ECHO when a TCB exists.
+ *
+ * Returns value representing action to be taken.   These action values
+ * correspond to Action/Description values in RFC 2960, Table 2.
+ */
+static char sctp_tietags_compare(struct sctp_association *new_asoc,
+				 const struct sctp_association *asoc)
+{
+	/* In this case, the peer may have restarted.  */
+	if ((asoc->c.my_vtag != new_asoc->c.my_vtag) &&
+	    (asoc->c.peer_vtag != new_asoc->c.peer_vtag) &&
+	    (asoc->c.my_vtag == new_asoc->c.my_ttag) &&
+	    (asoc->c.peer_vtag == new_asoc->c.peer_ttag))
+		return 'A';
+
+	/* Collision case B. */
+	if ((asoc->c.my_vtag == new_asoc->c.my_vtag) &&
+	    ((asoc->c.peer_vtag != new_asoc->c.peer_vtag) ||
+	     (0 == asoc->c.peer_vtag))) {
+		return 'B';
+	}
+
+	/* Collision case D. */
+	if ((asoc->c.my_vtag == new_asoc->c.my_vtag) &&
+	    (asoc->c.peer_vtag == new_asoc->c.peer_vtag))
+		return 'D';
+
+	/* Collision case C. */
+	if ((asoc->c.my_vtag != new_asoc->c.my_vtag) &&
+	    (asoc->c.peer_vtag == new_asoc->c.peer_vtag) &&
+	    (0 == new_asoc->c.my_ttag) &&
+	    (0 == new_asoc->c.peer_ttag))
+		return 'C';
+
+	/* No match to any of the special cases; discard this packet. */
+	return 'E';
+}
+
+/* Common helper routine for both duplicate and simulataneous INIT
+ * chunk handling.
+ */
+static sctp_disposition_t sctp_sf_do_unexpected_init(
+	const struct sctp_endpoint *ep,
+	const struct sctp_association *asoc,
+	const sctp_subtype_t type,
+	void *arg, sctp_cmd_seq_t *commands)
+{
+	sctp_disposition_t retval;
+	sctp_chunk_t *chunk = arg;
+	sctp_chunk_t *repl;
+	struct sctp_association *new_asoc;
+	sctp_chunk_t *err_chunk;
+	struct sctp_packet *packet;
+	sctp_unrecognized_param_t *unk_param;
+	int len;
+
+	/* 6.10 Bundling
+	 * An endpoint MUST NOT bundle INIT, INIT ACK or
+	 * SHUTDOWN COMPLETE with any other chunks.
+	 */
+	if (!chunk->singleton)
+		return SCTP_DISPOSITION_VIOLATION;
+
+	/* Grab the INIT header.  */
+	chunk->subh.init_hdr = (sctp_inithdr_t *) chunk->skb->data;
+
+	/* Tag the variable length parameters.  */
+	chunk->param_hdr.v = skb_pull(chunk->skb, sizeof(sctp_inithdr_t));
+
+	/* Verify the INIT chunk before processing it. */
+	err_chunk = NULL;
+	if (!sctp_verify_init(asoc, chunk->chunk_hdr->type,
+			      (sctp_init_chunk_t *)chunk->chunk_hdr, chunk,
+			      &err_chunk)) {
+		/* This chunk contains fatal error. It is to be discarded.
+		 * Send an ABORT, with causes if there is any.
+		 */
+		if (err_chunk) {
+			packet = sctp_abort_pkt_new(ep, asoc, arg,
+					(__u8 *)(err_chunk->chunk_hdr) +
+					sizeof(sctp_chunkhdr_t),
+					ntohs(err_chunk->chunk_hdr->length) -
+					sizeof(sctp_chunkhdr_t));
+
+			if (packet) {
+				sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
+						SCTP_PACKET(packet));
+				SCTP_INC_STATS(SctpOutCtrlChunks);
+				retval = SCTP_DISPOSITION_CONSUME;
+			} else {
+				retval = SCTP_DISPOSITION_NOMEM;
+			}
+			goto cleanup;
+		} else {
+			return sctp_sf_tabort_8_4_8(ep, asoc, type, arg,
+						    commands);
+		}
+	}
+
+	/*
+	 * Other parameters for the endpoint SHOULD be copied from the
+	 * existing parameters of the association (e.g. number of
+	 * outbound streams) into the INIT ACK and cookie.
+	 * FIXME:  We are copying parameters from the endpoint not the
+	 * association.
+	 */
+	new_asoc = sctp_make_temp_asoc(ep, chunk, GFP_ATOMIC);
+	if (!new_asoc)
+		goto nomem;
+
+	/* In the outbound INIT ACK the endpoint MUST copy its current
+	 * Verification Tag and Peers Verification tag into a reserved
+	 * place (local tie-tag and per tie-tag) within the state cookie.
+	 */
+	if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
+			       sctp_source(chunk),
+			       (sctp_init_chunk_t *)chunk->chunk_hdr,
+			       GFP_ATOMIC)) {
+		retval = SCTP_DISPOSITION_NOMEM;
+		goto nomem_init;
+	}
+
+	/* Make sure no new addresses are being added during the
+	 * restart.   Do not do this check for COOKIE-WAIT state,
+	 * since there are no peer addresses to check against.
+	 * Upon return an ABORT will have been sent if needed.
+	 */
+	if (asoc->state != SCTP_STATE_COOKIE_WAIT) {
+		if (!sctp_sf_check_restart_addrs(new_asoc, asoc, chunk,
+						 commands)) {
+			retval = SCTP_DISPOSITION_CONSUME;
+			goto cleanup_asoc;
+		}
+	}
+
+	sctp_tietags_populate(new_asoc, asoc);
+
+	/* B) "Z" shall respond immediately with an INIT ACK chunk.  */
+
+	/* If there are errors need to be reported for unknown parameters,
+	 * make sure to reserve enough room in the INIT ACK for them.
+	 */
+	len = 0;
+	if (err_chunk) {
+		len = ntohs(err_chunk->chunk_hdr->length) -
+			sizeof(sctp_chunkhdr_t);
+	}
+
+	if (sctp_assoc_set_bind_addr_from_ep(new_asoc, GFP_ATOMIC) < 0)
+		goto nomem;
+
+	repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC, len);
+	if (!repl)
+		goto nomem;
+
+	/* If there are errors need to be reported for unknown parameters,
+	 * include them in the outgoing INIT ACK as "Unrecognized parameter"
+	 * parameter.
+	 */
+	if (err_chunk) {
+		/* Get the "Unrecognized parameter" parameter(s) out of the
+		 * ERROR chunk generated by sctp_verify_init(). Since the
+		 * error cause code for "unknown parameter" and the
+		 * "Unrecognized parameter" type is the same, we can
+		 * construct the parameters in INIT ACK by copying the
+		 * ERROR causes over.
+		 */
+		unk_param = (sctp_unrecognized_param_t *)
+			    ((__u8 *)(err_chunk->chunk_hdr) +
+			    sizeof(sctp_chunkhdr_t));
+		/* Replace the cause code with the "Unrecognized parameter"
+		 * parameter type.
+		 */
+		sctp_addto_chunk(repl, len, unk_param);
+		sctp_free_chunk(err_chunk);
+	}
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc));
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
+
+	/*
+	 * Note: After sending out INIT ACK with the State Cookie parameter,
+	 * "Z" MUST NOT allocate any resources for this new association.
+	 * Otherwise, "Z" will be vulnerable to resource attacks.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
+	retval = SCTP_DISPOSITION_CONSUME;
+
+cleanup:
+	if (err_chunk)
+		sctp_free_chunk(err_chunk);
+	return retval;
+nomem:
+	retval = SCTP_DISPOSITION_NOMEM;
+	goto cleanup;
+nomem_init:
+cleanup_asoc:
+	sctp_association_free(new_asoc);
+	goto cleanup;
+}
+
+/*
+ * Handle simultanous INIT.
+ * This means we started an INIT and then we got an INIT request from
+ * our peer.
+ *
+ * Section: 5.2.1 INIT received in COOKIE-WAIT or COOKIE-ECHOED State (Item B)
+ * This usually indicates an initialization collision, i.e., each
+ * endpoint is attempting, at about the same time, to establish an
+ * association with the other endpoint.
+ *
+ * Upon receipt of an INIT in the COOKIE-WAIT or COOKIE-ECHOED state, an
+ * endpoint MUST respond with an INIT ACK using the same parameters it
+ * sent in its original INIT chunk (including its Verification Tag,
+ * unchanged). These original parameters are combined with those from the
+ * newly received INIT chunk. The endpoint shall also generate a State
+ * Cookie with the INIT ACK. The endpoint uses the parameters sent in its
+ * INIT to calculate the State Cookie.
+ *
+ * After that, the endpoint MUST NOT change its state, the T1-init
+ * timer shall be left running and the corresponding TCB MUST NOT be
+ * destroyed. The normal procedures for handling State Cookies when
+ * a TCB exists will resolve the duplicate INITs to a single association.
+ *
+ * For an endpoint that is in the COOKIE-ECHOED state it MUST populate
+ * its Tie-Tags with the Tag information of itself and its peer (see
+ * section 5.2.2 for a description of the Tie-Tags).
+ *
+ * Verification Tag: Not explicit, but an INIT can not have a valid
+ * verification tag, so we skip the check.
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_do_5_2_1_siminit(const struct sctp_endpoint *ep,
+				    const struct sctp_association *asoc,
+				    const sctp_subtype_t type,
+				    void *arg,
+				    sctp_cmd_seq_t *commands)
+{
+	/* Call helper to do the real work for both simulataneous and
+	 * duplicate INIT chunk handling.
+	 */
+	return sctp_sf_do_unexpected_init(ep, asoc, type, arg, commands);
+}
+
+/*
+ * Handle duplicated INIT messages.  These are usually delayed
+ * restransmissions.
+ *
+ * Section: 5.2.2 Unexpected INIT in States Other than CLOSED,
+ * COOKIE-ECHOED and COOKIE-WAIT
+ *
+ * Unless otherwise stated, upon reception of an unexpected INIT for
+ * this association, the endpoint shall generate an INIT ACK with a
+ * State Cookie.  In the outbound INIT ACK the endpoint MUST copy its
+ * current Verification Tag and peer's Verification Tag into a reserved
+ * place within the state cookie.  We shall refer to these locations as
+ * the Peer's-Tie-Tag and the Local-Tie-Tag.  The outbound SCTP packet
+ * containing this INIT ACK MUST carry a Verification Tag value equal to
+ * the Initiation Tag found in the unexpected INIT.  And the INIT ACK
+ * MUST contain a new Initiation Tag (randomly generated see Section
+ * 5.3.1).  Other parameters for the endpoint SHOULD be copied from the
+ * existing parameters of the association (e.g. number of outbound
+ * streams) into the INIT ACK and cookie.
+ *
+ * After sending out the INIT ACK, the endpoint shall take no further
+ * actions, i.e., the existing association, including its current state,
+ * and the corresponding TCB MUST NOT be changed.
+ *
+ * Note: Only when a TCB exists and the association is not in a COOKIE-
+ * WAIT state are the Tie-Tags populated.  For a normal association INIT
+ * (i.e. the endpoint is in a COOKIE-WAIT state), the Tie-Tags MUST be
+ * set to 0 (indicating that no previous TCB existed).  The INIT ACK and
+ * State Cookie are populated as specified in section 5.2.1.
+ *
+ * Verification Tag: Not specified, but an INIT has no way of knowing
+ * what the verification tag could be, so we ignore it.
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_do_5_2_2_dupinit(const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					const sctp_subtype_t type,
+					void *arg,
+					sctp_cmd_seq_t *commands)
+{
+	/* Call helper to do the real work for both simulataneous and
+	 * duplicate INIT chunk handling.
+	 */
+	return sctp_sf_do_unexpected_init(ep, asoc, type, arg, commands);
+}
+
+
+
+/* Unexpected COOKIE-ECHO handler for peer restart (Table 2, action 'A')
+ *
+ * Section 5.2.4
+ *  A)  In this case, the peer may have restarted.
+ */
+static sctp_disposition_t sctp_sf_do_dupcook_a(const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					sctp_chunk_t *chunk,
+					sctp_cmd_seq_t *commands,
+					struct sctp_association *new_asoc)
+{
+	sctp_init_chunk_t *peer_init;
+	struct sctp_ulpevent *ev;
+	sctp_chunk_t *repl;
+
+	/* new_asoc is a brand-new association, so these are not yet
+	 * side effects--it is safe to run them here.
+	 */
+	peer_init = &chunk->subh.cookie_hdr->c.peer_init[0];
+
+	if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
+			       sctp_source(chunk), peer_init,
+			       GFP_ATOMIC))
+		goto nomem;
+
+	/* Make sure no new addresses are being added during the
+	 * restart.  Though this is a pretty complicated attack
+	 * since you'd have to get inside the cookie.
+	 */
+	if (!sctp_sf_check_restart_addrs(new_asoc, asoc, chunk, commands)) {
+		return SCTP_DISPOSITION_CONSUME;
+	}
+
+	/* For now, fail any unsent/unacked data.  Consider the optional
+	 * choice of resending of this data.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_PURGE_OUTQUEUE, SCTP_NULL());
+
+	/* Update the content of current association. */
+	sctp_add_cmd_sf(commands, SCTP_CMD_UPDATE_ASSOC, SCTP_ASOC(new_asoc));
+
+	repl = sctp_make_cookie_ack(new_asoc, chunk);
+	if (!repl)
+		goto nomem;
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
+
+	/* Report association restart to upper layer. */
+	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);
+	if (!ev)
+		goto nomem_ev;
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev));
+	return SCTP_DISPOSITION_CONSUME;
+
+nomem_ev:
+	sctp_free_chunk(repl);
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/* Unexpected COOKIE-ECHO handler for setup collision (Table 2, action 'B')
+ *
+ * Section 5.2.4
+ *   B) In this case, both sides may be attempting to start an association
+ *      at about the same time but the peer endpoint started its INIT
+ *      after responding to the local endpoint's INIT
+ */
+/* This case represents an initialization collision.  */
+static sctp_disposition_t sctp_sf_do_dupcook_b(const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					sctp_chunk_t *chunk,
+					sctp_cmd_seq_t *commands,
+					struct sctp_association *new_asoc)
+{
+	sctp_init_chunk_t *peer_init;
+	struct sctp_ulpevent *ev;
+	sctp_chunk_t *repl;
+
+	/* new_asoc is a brand-new association, so these are not yet
+	 * side effects--it is safe to run them here.
+	 */
+	peer_init = &chunk->subh.cookie_hdr->c.peer_init[0];
+	if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
+			       sctp_source(chunk), peer_init,
+			       GFP_ATOMIC))
+		goto nomem;
+
+	/* Update the content of current association.  */
+	sctp_add_cmd_sf(commands, SCTP_CMD_UPDATE_ASSOC, SCTP_ASOC(new_asoc));
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+			SCTP_STATE(SCTP_STATE_ESTABLISHED));
+	SCTP_INC_STATS(SctpCurrEstab);
+	sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, SCTP_NULL());
+
+	repl = sctp_make_cookie_ack(new_asoc, chunk);
+	if (!repl)
+		goto nomem;
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
+	sctp_add_cmd_sf(commands, SCTP_CMD_TRANSMIT, SCTP_NULL());
+
+	/* RFC 2960 5.1 Normal Establishment of an Association
+	 *
+	 * D) IMPLEMENTATION NOTE: An implementation may choose to
+	 * send the Communication Up notification to the SCTP user
+	 * upon reception of a valid COOKIE ECHO chunk.
+	 */
+	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);
+	if (!ev)
+		goto nomem_ev;
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev));
+	return SCTP_DISPOSITION_CONSUME;
+
+nomem_ev:
+	sctp_free_chunk(repl);
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/* Unexpected COOKIE-ECHO handler for setup collision (Table 2, action 'C')
+ *
+ * Section 5.2.4
+ *  C) In this case, the local endpoint's cookie has arrived late.
+ *     Before it arrived, the local endpoint sent an INIT and received an
+ *     INIT-ACK and finally sent a COOKIE ECHO with the peer's same tag
+ *     but a new tag of its own.
+ */
+/* This case represents an initialization collision.  */
+static sctp_disposition_t sctp_sf_do_dupcook_c(const struct sctp_endpoint *ep,
+				      const struct sctp_association *asoc,
+				      sctp_chunk_t *chunk,
+				      sctp_cmd_seq_t *commands,
+				      struct sctp_association *new_asoc)
+{
+	/* The cookie should be silently discarded.
+	 * The endpoint SHOULD NOT change states and should leave
+	 * any timers running.
+	 */
+	return SCTP_DISPOSITION_DISCARD;
+}
+
+/* Unexpected COOKIE-ECHO handler lost chunk (Table 2, action 'D')
+ *
+ * Section 5.2.4
+ *
+ * D) When both local and remote tags match the endpoint should always
+ *    enter the ESTABLISHED state, if it has not already done so.
+ */
+/* This case represents an initialization collision.  */
+static sctp_disposition_t sctp_sf_do_dupcook_d(const struct sctp_endpoint *ep,
+				      const struct sctp_association *asoc,
+				      sctp_chunk_t *chunk,
+				      sctp_cmd_seq_t *commands,
+				      struct sctp_association *new_asoc)
+{
+	struct sctp_ulpevent *ev = NULL;
+	sctp_chunk_t *repl;
+
+	/* Clarification from Implementor's Guide:
+	 * D) When both local and remote tags match the endpoint should
+         * enter the ESTABLISHED state, if it is in the COOKIE-ECHOED state.
+         * It should stop any cookie timer that may be running and send
+         * a COOKIE ACK.
+	 */
+
+	/* Don't accidentally move back into established state. */
+	if (asoc->state < SCTP_STATE_ESTABLISHED) {
+		sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+				SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE));
+		sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+				SCTP_STATE(SCTP_STATE_ESTABLISHED));
+		SCTP_INC_STATS(SctpCurrEstab);
+		sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START,
+				SCTP_NULL());
+
+		/* RFC 2960 5.1 Normal Establishment of an Association
+		 *
+		 * D) IMPLEMENTATION NOTE: An implementation may choose
+		 * to send the Communication Up notification to the
+		 * SCTP user upon reception of a valid COOKIE
+		 * ECHO chunk.
+		 */
+		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);
+		if (!ev)
+			goto nomem;
+		sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
+				SCTP_ULPEVENT(ev));
+	}
+	sctp_add_cmd_sf(commands, SCTP_CMD_TRANSMIT, SCTP_NULL());
+
+	repl = sctp_make_cookie_ack(new_asoc, chunk);
+	if (!repl)
+		goto nomem;
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
+	sctp_add_cmd_sf(commands, SCTP_CMD_TRANSMIT, SCTP_NULL());
+
+	return SCTP_DISPOSITION_CONSUME;
+
+nomem:
+	if (ev)
+		sctp_ulpevent_free(ev);
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/*
+ * Handle a duplicate COOKIE-ECHO.  This usually means a cookie-carrying
+ * chunk was retransmitted and then delayed in the network.
+ *
+ * Section: 5.2.4 Handle a COOKIE ECHO when a TCB exists
+ *
+ * Verification Tag: None.  Do cookie validation.
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					const sctp_subtype_t type,
+					void *arg,
+					sctp_cmd_seq_t *commands)
+{
+	sctp_disposition_t retval;
+	sctp_chunk_t *chunk = arg;
+	struct sctp_association *new_asoc;
+	int error = 0;
+	char action;
+	sctp_chunk_t *err_chk_p;
+
+	/* "Decode" the chunk.  We have no optional parameters so we
+	 * are in good shape.
+	 */
+        chunk->subh.cookie_hdr = (sctp_signed_cookie_t *)chunk->skb->data;
+	skb_pull(chunk->skb, ntohs(chunk->chunk_hdr->length) -
+		 sizeof(sctp_chunkhdr_t));
+
+	/* In RFC 2960 5.2.4 3, if both Verification Tags in the State Cookie
+	 * of a duplicate COOKIE ECHO match the Verification Tags of the
+	 * current association, consider the State Cookie valid even if
+	 * the lifespan is exceeded.
+	 */
+	new_asoc = sctp_unpack_cookie(ep, asoc, chunk, GFP_ATOMIC, &error,
+				      &err_chk_p);
+
+	/* FIXME:
+	 * If the re-build failed, what is the proper error path
+	 * from here?
+	 *
+	 * [We should abort the association. --piggy]
+	 */
+	if (!new_asoc) {
+		/* FIXME: Several errors are possible.  A bad cookie should
+		 * be silently discarded, but think about logging it too.
+		 */
+		switch (error) {
+		case -SCTP_IERROR_NOMEM:
+			goto nomem;
+
+		case -SCTP_IERROR_STALE_COOKIE:
+			sctp_send_stale_cookie_err(ep, asoc, chunk, commands,
+						   err_chk_p);
+			return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+		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
+	 * current association.
+	 */
+	action = sctp_tietags_compare(new_asoc, asoc);
+
+	switch (action) {
+	case 'A': /* Association restart. */
+		retval = sctp_sf_do_dupcook_a(ep, asoc, chunk, commands,
+					      new_asoc);
+		break;
+
+	case 'B': /* Collision case B. */
+		retval = sctp_sf_do_dupcook_b(ep, asoc, chunk, commands,
+					      new_asoc);
+		break;
+
+	case 'C': /* Collision case C. */
+		retval = sctp_sf_do_dupcook_c(ep, asoc, chunk, commands,
+					      new_asoc);
+		break;
+
+	case 'D': /* Collision case D. */
+		retval = sctp_sf_do_dupcook_d(ep, asoc, chunk, commands,
+					      new_asoc);
+		break;
+
+	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));
+	sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
+
+	return retval;
+
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/*
+ * Process an ABORT.  (SHUTDOWN-PENDING state)
+ *
+ * See sctp_sf_do_9_1_abort().
+ */
+sctp_disposition_t sctp_sf_shutdown_pending_abort(
+	const struct sctp_endpoint *ep,
+	const struct sctp_association *asoc,
+	const sctp_subtype_t type,
+	void *arg,
+	sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+
+	if (!sctp_vtag_verify_either(chunk, asoc))
+		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+
+	/* Stop the T5-shutdown guard timer.  */
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD));
+
+	return sctp_sf_do_9_1_abort(ep, asoc, type, arg, commands);
+}
+
+/*
+ * Process an ABORT.  (SHUTDOWN-SENT state)
+ *
+ * See sctp_sf_do_9_1_abort().
+ */
+sctp_disposition_t sctp_sf_shutdown_sent_abort(const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					const sctp_subtype_t type,
+					void *arg,
+					sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+
+	if (!sctp_vtag_verify_either(chunk, asoc))
+		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+
+	/* Stop the T2-shutdown timer. */
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN));
+
+	/* Stop the T5-shutdown guard timer.  */
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD));
+
+	return sctp_sf_do_9_1_abort(ep, asoc, type, arg, commands);
+}
+
+/*
+ * Process an ABORT.  (SHUTDOWN-ACK-SENT state)
+ *
+ * See sctp_sf_do_9_1_abort().
+ */
+sctp_disposition_t sctp_sf_shutdown_ack_sent_abort(
+	const struct sctp_endpoint *ep,
+	const struct sctp_association *asoc,
+	const sctp_subtype_t type,
+	void *arg,
+	sctp_cmd_seq_t *commands)
+{
+	/* The same T2 timer, so we should be able to use
+	 * common function with the SHUTDOWN-SENT state.
+	 */
+	return sctp_sf_shutdown_sent_abort(ep, asoc, type, arg, commands);
+}
+
+/*
+ * Handle an Error received in COOKIE_ECHOED state.
+ *
+ * Only handle the error type of stale COOKIE Error, the other errors will
+ * be ignored.
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_cookie_echoed_err(const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					const sctp_subtype_t type,
+					void *arg,
+					sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+	sctp_errhdr_t *err;
+
+	err = (sctp_errhdr_t *)(chunk->skb->data);
+
+	/* If we have gotten too many failures, give up.  */
+	if (1 + asoc->counters[SCTP_COUNTER_INIT_ERROR] >
+					 asoc->max_init_attempts) {
+		/* INIT_FAILED will issue an ulpevent.  */
+		sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
+				SCTP_U32(err->cause));
+		return SCTP_DISPOSITION_DELETE_TCB;
+	}
+
+	/* Process the error here */
+	switch (err->cause) {
+	case SCTP_ERROR_STALE_COOKIE:
+		return sctp_sf_do_5_2_6_stale(ep, asoc, type, arg, commands);
+	default:
+		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+	}
+}
+
+/*
+ * Handle a Stale COOKIE Error
+ *
+ * Section: 5.2.6 Handle Stale COOKIE Error
+ * If the association is in the COOKIE-ECHOED state, the endpoint may elect
+ * one of the following three alternatives.
+ * ...
+ * 3) Send a new INIT chunk to the endpoint, adding a Cookie
+ *    Preservative parameter requesting an extension to the lifetime of
+ *    the State Cookie. When calculating the time extension, an
+ *    implementation SHOULD use the RTT information measured based on the
+ *    previous COOKIE ECHO / ERROR exchange, and should add no more
+ *    than 1 second beyond the measured RTT, due to long State Cookie
+ *    lifetimes making the endpoint more subject to a replay attack.
+ *
+ * Verification Tag:  Not explicit, but safe to ignore.
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_do_5_2_6_stale(const struct sctp_endpoint *ep,
+					  const struct sctp_association *asoc,
+					  const sctp_subtype_t type,
+					  void *arg,
+					  sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+	time_t stale;
+	sctp_cookie_preserve_param_t bht;
+	sctp_errhdr_t *err;
+	struct list_head *pos;
+	struct sctp_transport *t;
+	sctp_chunk_t *reply;
+	sctp_bind_addr_t *bp;
+	int attempts;
+
+	attempts = asoc->counters[SCTP_COUNTER_INIT_ERROR] + 1;
+
+	if (attempts >= asoc->max_init_attempts) {
+		sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
+				SCTP_U32(SCTP_ERROR_STALE_COOKIE));
+		return SCTP_DISPOSITION_DELETE_TCB;
+	}
+
+	err = (sctp_errhdr_t *)(chunk->skb->data);
+
+	/* When calculating the time extension, an implementation
+	 * SHOULD use the RTT information measured based on the
+	 * previous COOKIE ECHO / ERROR exchange, and should add no
+	 * more than 1 second beyond the measured RTT, due to long
+	 * State Cookie lifetimes making the endpoint more subject to
+	 * a replay attack.
+	 * Measure of Staleness's unit is usec. (1/1000000 sec)
+	 * Suggested Cookie Life-span Increment's unit is msec.
+	 * (1/1000 sec)
+	 * In general, if you use the suggested cookie life, the value
+	 * found in the field of measure of staleness should be doubled
+	 * to give ample time to retransmit the new cookie and thus
+	 * yield a higher probability of success on the reattempt.
+	 */
+	stale = ntohl(*(suseconds_t *)((u8 *)err + sizeof(sctp_errhdr_t)));
+	stale = stale << 1 / 1000;
+
+	bht.param_hdr.type = SCTP_PARAM_COOKIE_PRESERVATIVE;
+	bht.param_hdr.length = htons(sizeof(bht));
+	bht.lifespan_increment = htonl(stale);
+
+	/* Build that new INIT chunk.  */
+	bp = (sctp_bind_addr_t *) &asoc->base.bind_addr;
+	reply = sctp_make_init(asoc, bp, GFP_ATOMIC, sizeof(bht));
+	if (!reply)
+		goto nomem;
+
+	sctp_addto_chunk(reply, sizeof(bht), &bht);
+
+	/* Cast away the const modifier, as we want to just
+	 * rerun it through as a sideffect.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_COUNTER_INC,
+			SCTP_COUNTER(SCTP_COUNTER_INIT_ERROR));
+
+	/* If we've sent any data bundled with COOKIE-ECHO we need to
+	 * resend.
+	 */
+	list_for_each(pos, &asoc->peer.transport_addr_list) {
+		t = list_entry(pos, struct sctp_transport, transports);
+		sctp_add_cmd_sf(commands, SCTP_CMD_RETRAN, SCTP_TRANSPORT(t));
+	}
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE));
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+			SCTP_STATE(SCTP_STATE_COOKIE_WAIT));
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
+
+	return SCTP_DISPOSITION_CONSUME;
+
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/*
+ * Process an ABORT.
+ *
+ * Section: 9.1
+ * After checking the Verification Tag, the receiving endpoint shall
+ * remove the association from its record, and shall report the
+ * termination to its upper layer.
+ *
+ * Verification Tag: 8.5.1 Exceptions in Verification Tag Rules
+ * B) Rules for packet carrying ABORT:
+ *
+ *  - The endpoint shall always fill in the Verification Tag field of the
+ *    outbound packet with the destination endpoint's tag value if it
+ *    is known.
+ *
+ *  - If the ABORT is sent in response to an OOTB packet, the endpoint
+ *    MUST follow the procedure described in Section 8.4.
+ *
+ *  - The receiver MUST accept the packet if the Verification Tag
+ *    matches either its own tag, OR the tag of its peer. Otherwise, the
+ *    receiver MUST silently discard the packet and take no further
+ *    action.
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					const sctp_subtype_t type,
+					void *arg,
+					sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+	__u16 error = SCTP_ERROR_NO_ERROR;
+
+	if (!sctp_vtag_verify_either(chunk, asoc))
+		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+
+	if (chunk && (ntohs(chunk->chunk_hdr->length) >=
+			(sizeof(struct sctp_chunkhdr) +
+			sizeof(struct sctp_errhdr))))
+		error = ((sctp_errhdr_t *)chunk->skb->data)->cause;
+
+ 	/* ASSOC_FAILED will DELETE_TCB. */
+	sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_U32(error));
+	SCTP_INC_STATS(SctpAborteds);
+	SCTP_DEC_STATS(SctpCurrEstab);
+
+	/* BUG?  This does not look complete... */
+	return SCTP_DISPOSITION_ABORT;
+}
+
+/*
+ * Process an ABORT.  (COOKIE-WAIT state)
+ *
+ * See sctp_sf_do_9_1_abort() above.
+ */
+sctp_disposition_t sctp_sf_cookie_wait_abort(const struct sctp_endpoint *ep,
+				     const struct sctp_association *asoc,
+				     const sctp_subtype_t type,
+				     void *arg,
+				     sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+	__u16 error = SCTP_ERROR_NO_ERROR;
+
+	if (!sctp_vtag_verify_either(chunk, asoc))
+		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+			SCTP_STATE(SCTP_STATE_CLOSED));
+	SCTP_INC_STATS(SctpAborteds);
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
+
+	if (chunk && (ntohs(chunk->chunk_hdr->length) >=
+			(sizeof(struct sctp_chunkhdr) +
+			sizeof(struct sctp_errhdr))))
+		error = ((sctp_errhdr_t *)chunk->skb->data)->cause;
+
+	/* CMD_INIT_FAILED will DELETE_TCB. */
+	sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_U32(error));
+
+	return SCTP_DISPOSITION_ABORT;
+}
+
+/*
+ * Process an ABORT.  (COOKIE-ECHOED state)
+ *
+ * See sctp_sf_do_9_1_abort() above.
+ */
+sctp_disposition_t sctp_sf_cookie_echoed_abort(const struct sctp_endpoint *ep,
+					       const struct sctp_association *asoc,
+					       const sctp_subtype_t type,
+					       void *arg,
+					       sctp_cmd_seq_t *commands)
+{
+	/* There is a single T1 timer, so we should be able to use
+	 * common function with the COOKIE-WAIT state.
+	 */
+	return sctp_sf_cookie_wait_abort(ep, asoc, type, arg, commands);
+}
+
+/*
+ * sctp_sf_do_9_2_shut
+ *
+ * Section: 9.2
+ * Upon the reception of the SHUTDOWN, the peer endpoint shall
+ *  - enter the SHUTDOWN-RECEIVED state,
+ *
+ *  - stop accepting new data from its SCTP user
+ *
+ *  - verify, by checking the Cumulative TSN Ack field of the chunk,
+ *    that all its outstanding DATA chunks have been received by the
+ *    SHUTDOWN sender.
+ *
+ * Once an endpoint as reached the SHUTDOWN-RECEIVED state it MUST NOT
+ * send a SHUTDOWN in response to a ULP request. And should discard
+ * subsequent SHUTDOWN chunks.
+ *
+ * If there are still outstanding DATA chunks left, the SHUTDOWN
+ * receiver shall continue to follow normal data transmission
+ * procedures defined in Section 6 until all outstanding DATA chunks
+ * are acknowledged; however, the SHUTDOWN receiver MUST NOT accept
+ * new data from its SCTP user.
+ *
+ * Verification Tag:  8.5 Verification Tag [Normal verification]
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_do_9_2_shutdown(const struct sctp_endpoint *ep,
+					   const struct sctp_association *asoc,
+					   const sctp_subtype_t type,
+					   void *arg,
+					   sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+	sctp_shutdownhdr_t *sdh;
+	sctp_disposition_t disposition;
+
+	/* Convert the elaborate header.  */
+	sdh = (sctp_shutdownhdr_t *)chunk->skb->data;
+	skb_pull(chunk->skb, sizeof(sctp_shutdownhdr_t));
+	chunk->subh.shutdown_hdr = sdh;
+
+	/* 8.5 When receiving an SCTP packet, the endpoint MUST ensure
+	 * that the value in the Verification Tag field of the
+	 * received SCTP packet matches its own Tag. If the received
+	 * Verification Tag value does not match the receiver's own
+	 * tag value, the receiver shall silently discard the packet...
+	 */
+	if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag)
+		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+
+	/* Upon the reception of the SHUTDOWN, the peer endpoint shall
+	 *  - enter the SHUTDOWN-RECEIVED state,
+	 *  - stop accepting new data from its SCTP user
+	 *
+	 * [This is implicit in the new state.]
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+			SCTP_STATE(SCTP_STATE_SHUTDOWN_RECEIVED));
+	disposition = SCTP_DISPOSITION_CONSUME;
+
+	if (sctp_outq_is_empty(&asoc->outqueue)) {
+		disposition = sctp_sf_do_9_2_shutdown_ack(ep, asoc, type,
+							  arg, commands);
+	}
+
+	/*  - verify, by checking the Cumulative TSN Ack field of the
+	 *    chunk, that all its outstanding DATA chunks have been
+	 *    received by the SHUTDOWN sender.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_CTSN,
+			SCTP_U32(chunk->subh.shutdown_hdr->cum_tsn_ack));
+	return disposition;
+}
+
+/* RFC 2960 9.2
+ * If an endpoint is in SHUTDOWN-ACK-SENT state and receives an INIT chunk
+ * (e.g., if the SHUTDOWN COMPLETE was lost) with source and destination
+ * transport addresses (either in the IP addresses or in the INIT chunk)
+ * that belong to this association, it should discard the INIT chunk and
+ * retransmit the SHUTDOWN ACK chunk.
+ */
+sctp_disposition_t sctp_sf_do_9_2_reshutack(const struct sctp_endpoint *ep,
+				    const struct sctp_association *asoc,
+				    const sctp_subtype_t type,
+				    void *arg,
+				    sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = (sctp_chunk_t *) arg;
+	sctp_chunk_t *reply;
+
+	reply = sctp_make_shutdown_ack(asoc, chunk);
+	if (NULL == reply)
+		goto nomem;
+
+	/* Set the transport for the SHUTDOWN ACK chunk and the timeout for
+	 * the T2-SHUTDOWN timer.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_SETUP_T2, SCTP_CHUNK(reply));
+
+	/* and restart the T2-shutdown timer. */
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN));
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
+
+	return SCTP_DISPOSITION_CONSUME;
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/*
+ * sctp_sf_do_ecn_cwr
+ *
+ * Section:  Appendix A: Explicit Congestion Notification
+ *
+ * CWR:
+ *
+ * RFC 2481 details a specific bit for a sender to send in the header of
+ * its next outbound TCP segment to indicate to its peer that it has
+ * reduced its congestion window.  This is termed the CWR bit.  For
+ * SCTP the same indication is made by including the CWR chunk.
+ * This chunk contains one data element, i.e. the TSN number that
+ * was sent in the ECNE chunk.  This element represents the lowest
+ * TSN number in the datagram that was originally marked with the
+ * CE bit.
+ *
+ * Verification Tag: 8.5 Verification Tag [Normal verification]
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_do_ecn_cwr(const struct sctp_endpoint *ep,
+				      const struct sctp_association *asoc,
+				      const sctp_subtype_t type,
+				      void *arg,
+				      sctp_cmd_seq_t *commands)
+{
+	sctp_cwrhdr_t *cwr;
+	sctp_chunk_t *chunk = arg;
+
+	/* 8.5 When receiving an SCTP packet, the endpoint MUST ensure
+	 * that the value in the Verification Tag field of the
+	 * received SCTP packet matches its own Tag. If the received
+	 * Verification Tag value does not match the receiver's own
+	 * tag value, the receiver shall silently discard the packet...
+	 */
+	if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag)
+		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+
+	cwr = (sctp_cwrhdr_t *) chunk->skb->data;
+	skb_pull(chunk->skb, sizeof(sctp_cwrhdr_t));
+
+	cwr->lowest_tsn = ntohl(cwr->lowest_tsn);
+
+	/* Does this CWR ack the last sent congestion notification? */
+	if (TSN_lte(asoc->last_ecne_tsn, cwr->lowest_tsn)) {
+		/* Stop sending ECNE. */
+		sctp_add_cmd_sf(commands,
+				SCTP_CMD_ECN_CWR,
+				SCTP_U32(cwr->lowest_tsn));
+	}
+	return SCTP_DISPOSITION_CONSUME;
+}
+
+/*
+ * sctp_sf_do_ecne
+ *
+ * Section:  Appendix A: Explicit Congestion Notification
+ *
+ * ECN-Echo
+ *
+ * RFC 2481 details a specific bit for a receiver to send back in its
+ * TCP acknowledgements to notify the sender of the Congestion
+ * Experienced (CE) bit having arrived from the network.  For SCTP this
+ * same indication is made by including the ECNE chunk.  This chunk
+ * contains one data element, i.e. the lowest TSN associated with the IP
+ * datagram marked with the CE bit.....
+ *
+ * Verification Tag: 8.5 Verification Tag [Normal verification]
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_do_ecne(const struct sctp_endpoint *ep,
+				   const struct sctp_association *asoc,
+				   const sctp_subtype_t type,
+				   void *arg,
+				   sctp_cmd_seq_t *commands)
+{
+	sctp_ecnehdr_t *ecne;
+	sctp_chunk_t *chunk = arg;
+
+	/* 8.5 When receiving an SCTP packet, the endpoint MUST ensure
+	 * that the value in the Verification Tag field of the
+	 * received SCTP packet matches its own Tag. If the received
+	 * Verification Tag value does not match the receiver's own
+	 * tag value, the receiver shall silently discard the packet...
+	 */
+	if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag)
+		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+
+	ecne = (sctp_ecnehdr_t *) chunk->skb->data;
+	skb_pull(chunk->skb, sizeof(sctp_ecnehdr_t));
+
+	/* If this is a newer ECNE than the last CWR packet we sent out */
+	sctp_add_cmd_sf(commands, SCTP_CMD_ECN_ECNE,
+			SCTP_U32(ntohl(ecne->lowest_tsn)));
+
+	return SCTP_DISPOSITION_CONSUME;
+}
+
+/*
+ * Section: 6.2  Acknowledgement on Reception of DATA Chunks
+ *
+ * The SCTP endpoint MUST always acknowledge the reception of each valid
+ * DATA chunk.
+ *
+ * The guidelines on delayed acknowledgement algorithm specified in
+ * Section 4.2 of [RFC2581] SHOULD be followed. Specifically, an
+ * acknowledgement SHOULD be generated for at least every second packet
+ * (not every second DATA chunk) received, and SHOULD be generated within
+ * 200 ms of the arrival of any unacknowledged DATA chunk. In some
+ * situations it may be beneficial for an SCTP transmitter to be more
+ * conservative than the algorithms detailed in this document allow.
+ * However, an SCTP transmitter MUST NOT be more aggressive than the
+ * following algorithms allow.
+ *
+ * A SCTP receiver MUST NOT generate more than one SACK for every
+ * incoming packet, other than to update the offered window as the
+ * receiving application consumes new data.
+ *
+ * Verification Tag:  8.5 Verification Tag [Normal verification]
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					const sctp_subtype_t type,
+					void *arg,
+					sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+	sctp_datahdr_t *data_hdr;
+	sctp_chunk_t *err;
+	size_t datalen;
+	sctp_verb_t deliver;
+	int tmp;
+	__u32 tsn;
+
+	/* RFC 2960 8.5 Verification Tag
+	 *
+	 * When receiving an SCTP packet, the endpoint MUST ensure
+	 * that the value in the Verification Tag field of the
+	 * received SCTP packet matches its own Tag.
+	 */
+	if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) {
+		sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG,
+				SCTP_NULL());
+		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+        }
+
+	data_hdr = chunk->subh.data_hdr = (sctp_datahdr_t *)chunk->skb->data;
+	skb_pull(chunk->skb, sizeof(sctp_datahdr_t));
+
+	tsn = ntohl(data_hdr->tsn);
+
+	SCTP_DEBUG_PRINTK("eat_data: TSN 0x%x.\n", tsn);
+	SCTP_DEBUG_PRINTK("eat_data: skb->head %p.\n", chunk->skb->head);
+
+	/* ASSERT:  Now skb->data is really the user data.  */
+
+	/* Process ECN based congestion.
+	 *
+	 * Since the chunk structure is reused for all chunks within
+	 * a packet, we use ecn_ce_done to track if we've already
+	 * done CE processing for this packet.
+	 *
+	 * We need to do ECN processing even if we plan to discard the
+	 * chunk later.
+	 */
+
+	if (!chunk->ecn_ce_done) {
+		chunk->ecn_ce_done = 1;
+		if (INET_ECN_is_ce(chunk->skb->nh.iph->tos) &&
+		    asoc->peer.ecn_capable) {
+			/* Do real work as sideffect. */
+			sctp_add_cmd_sf(commands, SCTP_CMD_ECN_CE,
+					SCTP_U32(tsn));
+		}
+	}
+
+	tmp = sctp_tsnmap_check(&asoc->peer.tsn_map, tsn);
+	if (tmp < 0) {
+		/* The TSN is too high--silently discard the chunk and
+		 * count on it getting retransmitted later.
+		 */
+		goto discard_noforce;
+	} else if (tmp > 0) {
+		/* This is a duplicate.  Record it.  */
+		sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_DUP, SCTP_U32(tsn));
+		goto discard_force;
+	}
+
+	/* This is a new TSN.  */
+
+	/* Discard if there is no room in the receive window.
+	 * Actually, allow a little bit of overflow (up to a MTU).
+	 */
+	datalen = ntohs(chunk->chunk_hdr->length);
+	datalen -= sizeof(sctp_data_chunk_t);
+
+	deliver = SCTP_CMD_CHUNK_ULP;
+
+	/* Think about partial delivery. */
+	if ((datalen >= asoc->rwnd) && (!asoc->ulpq.pd_mode)) {
+
+		/* Even if we don't accept this chunk there is
+		 * memory pressure.
+		 */
+		sctp_add_cmd_sf(commands, SCTP_CMD_PART_DELIVER, SCTP_NULL());
+	}
+
+        /* Spill over rwnd a little bit.  Note: While allowed, this spill over
+	 * seems a bit troublesome in that frag_point varies based on
+	 * PMTU.  In cases, such as loopback, this might be a rather
+	 * large spill over.
+	 */
+	if (asoc->rwnd_over || (datalen > asoc->rwnd + asoc->frag_point)) {
+
+		/* If this is the next TSN, consider reneging to make
+		 * room.   Note: Playing nice with a confused sender.  A
+		 * malicious sender can still eat up all our buffer
+		 * space and in the future we may want to detect and
+		 * do more drastic reneging.
+		 */
+		if (sctp_tsnmap_has_gap(&asoc->peer.tsn_map) &&
+		    (sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1) == tsn) {
+			SCTP_DEBUG_PRINTK("Reneging for tsn:%u\n", tsn);
+			deliver = SCTP_CMD_RENEGE;
+		} else {
+			SCTP_DEBUG_PRINTK("Discard tsn: %u len: %Zd, "
+					  "rwnd: %d\n", tsn, datalen,
+					  asoc->rwnd);
+			goto discard_force;
+		}
+	}
+
+	/*
+	 * Section 3.3.10.9 No User Data (9)
+	 *
+	 * Cause of error
+	 * ---------------
+	 * No User Data:  This error cause is returned to the originator of a
+	 * DATA chunk if a received DATA chunk has no user data.
+	 */
+	if (unlikely(0 == datalen)) {
+		err = sctp_make_abort_no_data(asoc, chunk, tsn);
+		if (err) {
+			sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
+					SCTP_CHUNK(err));
+		}
+		/* We are going to ABORT, so we might as well stop
+		 * processing the rest of the chunks in the packet.
+		 */
+		sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL());
+		sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
+				SCTP_U32(SCTP_ERROR_NO_DATA));
+		SCTP_INC_STATS(SctpAborteds);
+		SCTP_DEC_STATS(SctpCurrEstab);
+		return SCTP_DISPOSITION_CONSUME;
+	}
+
+	/* If definately accepting the DATA chunk, record its TSN, otherwise
+	 * wait for renege processing.
+	 */
+	if (SCTP_CMD_CHUNK_ULP == deliver)
+		sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_TSN, SCTP_U32(tsn));
+
+	/* Note: Some chunks may get overcounted (if we drop) or overcounted
+	 * if we renege and the chunk arrives again.
+	 */
+	if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
+		SCTP_INC_STATS(SctpInUnorderChunks);
+	else
+		SCTP_INC_STATS(SctpInOrderChunks);
+
+	/* RFC 2960 6.5 Stream Identifier and Stream Sequence Number
+	 *
+	 * If an endpoint receive a DATA chunk with an invalid stream
+	 * identifier, it shall acknowledge the reception of the DATA chunk
+	 * following the normal procedure, immediately send an ERROR chunk
+	 * with cause set to "Invalid Stream Identifier" (See Section 3.3.10)
+	 * and discard the DATA chunk.
+	 */
+	if (ntohs(data_hdr->stream) >= asoc->c.sinit_max_instreams) {
+		err = sctp_make_op_error(asoc, chunk, SCTP_ERROR_INV_STRM,
+					 &data_hdr->stream,
+					 sizeof(data_hdr->stream));
+		if (err)
+			sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
+					SCTP_CHUNK(err));
+		goto discard_noforce;
+	}
+
+	/* Send the data up to the user.  Note:  Schedule  the
+	 * SCTP_CMD_CHUNK_ULP cmd before the SCTP_CMD_GEN_SACK, as the SACK
+	 * chunk needs the updated rwnd.
+	 */
+	sctp_add_cmd_sf(commands, deliver, SCTP_CHUNK(chunk));
+
+	if (asoc->autoclose) {
+		sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
+				SCTP_TO(SCTP_EVENT_TIMEOUT_AUTOCLOSE));
+	}
+
+	/* If this is the last chunk in a packet, we need to count it
+	 * toward sack generation.  Note that we need to SACK every
+	 * OTHER packet containing data chunks, EVEN IF WE DISCARD
+	 * THEM.  We elect to NOT generate SACK's if the chunk fails
+	 * the verification tag test.
+	 *
+	 * RFC 2960 6.2 Acknowledgement on Reception of DATA Chunks
+	 *
+	 * The SCTP endpoint MUST always acknowledge the reception of
+	 * each valid DATA chunk.
+	 *
+	 * The guidelines on delayed acknowledgement algorithm
+	 * specified in  Section 4.2 of [RFC2581] SHOULD be followed.
+	 * Specifically, an acknowledgement SHOULD be generated for at
+	 * least every second packet (not every second DATA chunk)
+	 * received, and SHOULD be generated within 200 ms of the
+	 * arrival of any unacknowledged DATA chunk.  In some
+	 * situations it may be beneficial for an SCTP transmitter to
+	 * be more conservative than the algorithms detailed in this
+	 * document allow. However, an SCTP transmitter MUST NOT be
+	 * more aggressive than the following algorithms allow.
+	 */
+	if (chunk->end_of_packet) {
+		sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_NOFORCE());
+
+		/* Start the SACK timer.  */
+		sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
+				SCTP_TO(SCTP_EVENT_TIMEOUT_SACK));
+	}
+
+	return SCTP_DISPOSITION_CONSUME;
+
+discard_force:
+	/* RFC 2960 6.2 Acknowledgement on Reception of DATA Chunks
+	 *
+	 * When a packet arrives with duplicate DATA chunk(s) and with
+	 * no new DATA chunk(s), the endpoint MUST immediately send a
+	 * SACK with no delay.  If a packet arrives with duplicate
+	 * DATA chunk(s) bundled with new DATA chunks, the endpoint
+	 * MAY immediately send a SACK.  Normally receipt of duplicate
+	 * DATA chunks will occur when the original SACK chunk was lost
+	 * and the peer's RTO has expired.  The duplicate TSN number(s)
+	 * SHOULD be reported in the SACK as duplicate.
+	 */
+	/* In our case, we split the MAY SACK advice up whether or not
+	 * the last chunk is a duplicate.'
+	 */
+	if (chunk->end_of_packet)
+		sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_FORCE());
+	return SCTP_DISPOSITION_DISCARD;
+
+discard_noforce:
+	if (chunk->end_of_packet) {
+		sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_NOFORCE());
+
+		/* Start the SACK timer.  */
+		sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
+				SCTP_TO(SCTP_EVENT_TIMEOUT_SACK));
+	}
+	return SCTP_DISPOSITION_DISCARD;
+}
+
+/*
+ * sctp_sf_eat_data_fast_4_4
+ *
+ * Section: 4 (4)
+ * (4) In SHUTDOWN-SENT state the endpoint MUST acknowledge any received
+ *    DATA chunks without delay.
+ *
+ * Verification Tag:  8.5 Verification Tag [Normal verification]
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_eat_data_fast_4_4(const struct sctp_endpoint *ep,
+				     const struct sctp_association *asoc,
+				     const sctp_subtype_t type,
+				     void *arg,
+				     sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+	sctp_datahdr_t *data_hdr;
+	sctp_chunk_t *err;
+	size_t datalen;
+	int tmp;
+	__u32 tsn;
+
+	/* RFC 2960 8.5 Verification Tag
+	 *
+	 * When receiving an SCTP packet, the endpoint MUST ensure
+	 * that the value in the Verification Tag field of the
+	 * received SCTP packet matches its own Tag.
+	 */
+	if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) {
+		sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG,
+				SCTP_NULL());
+		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+	}
+
+	data_hdr = chunk->subh.data_hdr = (sctp_datahdr_t *) chunk->skb->data;
+	skb_pull(chunk->skb, sizeof(sctp_datahdr_t));
+
+	tsn = ntohl(data_hdr->tsn);
+
+	SCTP_DEBUG_PRINTK("eat_data: TSN 0x%x.\n", tsn);
+
+	/* ASSERT:  Now skb->data is really the user data.  */
+
+	/* Process ECN based congestion.
+	 *
+	 * Since the chunk structure is reused for all chunks within
+	 * a packet, we use ecn_ce_done to track if we've already
+	 * done CE processing for this packet.
+	 *
+	 * We need to do ECN processing even if we plan to discard the
+	 * chunk later.
+	 */
+	if (!chunk->ecn_ce_done) {
+		chunk->ecn_ce_done = 1;
+		if (INET_ECN_is_ce(chunk->skb->nh.iph->tos) &&
+		    asoc->peer.ecn_capable) {
+			/* Do real work as sideffect. */
+			sctp_add_cmd_sf(commands, SCTP_CMD_ECN_CE,
+					SCTP_U32(tsn));
+		}
+	}
+
+	tmp = sctp_tsnmap_check(&asoc->peer.tsn_map, tsn);
+	if (tmp < 0) {
+		/* The TSN is too high--silently discard the chunk and
+		 * count on it getting retransmitted later.
+		 */
+		goto gen_shutdown;
+	} else if (tmp > 0) {
+		/* This is a duplicate.  Record it.  */
+		sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_DUP, SCTP_U32(tsn));
+		goto gen_shutdown;
+	}
+
+	/* This is a new TSN.  */
+
+	datalen = ntohs(chunk->chunk_hdr->length);
+	datalen -= sizeof(sctp_data_chunk_t);
+
+	/*
+	 * Section 3.3.10.9 No User Data (9)
+	 *
+	 * Cause of error
+	 * ---------------
+	 * No User Data:  This error cause is returned to the originator of a
+	 * DATA chunk if a received DATA chunk has no user data.
+	 */
+	if (unlikely(0 == datalen)) {
+		err = sctp_make_abort_no_data(asoc, chunk, tsn);
+		if (err) {
+			sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
+					SCTP_CHUNK(err));
+		}
+		/* We are going to ABORT, so we might as well stop
+		 * processing the rest of the chunks in the packet.
+		 */
+		sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL());
+		sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
+				SCTP_U32(SCTP_ERROR_NO_DATA));
+		SCTP_INC_STATS(SctpAborteds);
+		SCTP_DEC_STATS(SctpCurrEstab);
+		return SCTP_DISPOSITION_CONSUME;
+	}
+
+	/* We are accepting this DATA chunk. */
+
+	/* Record the fact that we have received this TSN.  */
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_TSN, SCTP_U32(tsn));
+
+	if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
+		SCTP_INC_STATS(SctpInUnorderChunks);
+	else
+		SCTP_INC_STATS(SctpInOrderChunks);
+
+	/* RFC 2960 6.5 Stream Identifier and Stream Sequence Number
+	 *
+	 * If an endpoint receive a DATA chunk with an invalid stream
+	 * identifier, it shall acknowledge the reception of the DATA chunk
+	 * following the normal procedure, immediately send an ERROR chunk
+	 * with cause set to "Invalid Stream Identifier" (See Section 3.3.10)
+	 * and discard the DATA chunk.
+	 */
+	if (ntohs(data_hdr->stream) >= asoc->c.sinit_max_instreams) {
+		err = sctp_make_op_error(asoc, chunk, SCTP_ERROR_INV_STRM,
+					 &data_hdr->stream,
+					 sizeof(data_hdr->stream));
+		if (err) {
+			sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
+					SCTP_CHUNK(err));
+		}
+	}
+
+	/* Go a head and force a SACK, since we are shutting down. */
+gen_shutdown:
+	/* Implementor's Guide.
+	 *
+	 * While in SHUTDOWN-SENT state, the SHUTDOWN sender MUST immediately
+	 * respond to each received packet containing one or more DATA chunk(s)
+	 * with a SACK, a SHUTDOWN chunk, and restart the T2-shutdown timer
+	 */
+	if (chunk->end_of_packet) {
+		/* We must delay the chunk creation since the cumulative
+		 * TSN has not been updated yet.
+		 */
+		sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SHUTDOWN, SCTP_NULL());
+		sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_FORCE());
+		sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
+				SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN));
+	}
+	return SCTP_DISPOSITION_CONSUME;
+}
+
+/*
+ * Section: 6.2  Processing a Received SACK
+ * D) Any time a SACK arrives, the endpoint performs the following:
+ *
+ *     i) If Cumulative TSN Ack is less than the Cumulative TSN Ack Point,
+ *     then drop the SACK.   Since Cumulative TSN Ack is monotonically
+ *     increasing, a SACK whose Cumulative TSN Ack is less than the
+ *     Cumulative TSN Ack Point indicates an out-of-order SACK.
+ *
+ *     ii) Set rwnd equal to the newly received a_rwnd minus the number
+ *     of bytes still outstanding after processing the Cumulative TSN Ack
+ *     and the Gap Ack Blocks.
+ *
+ *     iii) If the SACK is missing a TSN that was previously
+ *     acknowledged via a Gap Ack Block (e.g., the data receiver
+ *     reneged on the data), then mark the corresponding DATA chunk
+ *     as available for retransmit:  Mark it as missing for fast
+ *     retransmit as described in Section 7.2.4 and if no retransmit
+ *     timer is running for the destination address to which the DATA
+ *     chunk was originally transmitted, then T3-rtx is started for
+ *     that destination address.
+ *
+ * Verification Tag:  8.5 Verification Tag [Normal verification]
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_eat_sack_6_2(const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					const sctp_subtype_t type,
+					void *arg,
+					sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+	sctp_sackhdr_t *sackh;
+	__u32 ctsn;
+
+	/* 8.5 When receiving an SCTP packet, the endpoint MUST ensure
+	 * that the value in the Verification Tag field of the
+	 * received SCTP packet matches its own Tag. ...
+	 */
+	if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag)
+		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+
+	/* Pull the SACK chunk from the data buffer */
+	sackh = sctp_sm_pull_sack(chunk);
+	chunk->subh.sack_hdr = sackh;
+	ctsn = ntohl(sackh->cum_tsn_ack);
+
+	/* i) If Cumulative TSN Ack is less than the Cumulative TSN
+	 *     Ack Point, then drop the SACK.  Since Cumulative TSN
+	 *     Ack is monotonically increasing, a SACK whose
+	 *     Cumulative TSN Ack is less than the Cumulative TSN Ack
+	 *     Point indicates an out-of-order SACK.
+	 */
+	if (TSN_lt(ctsn, asoc->ctsn_ack_point)) {
+		SCTP_DEBUG_PRINTK("ctsn %x\n", ctsn);
+		SCTP_DEBUG_PRINTK("ctsn_ack_point %x\n", asoc->ctsn_ack_point);
+		return SCTP_DISPOSITION_DISCARD;
+	}
+
+	/* Return this SACK for further processing.  */
+	sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_SACK, SCTP_SACKH(sackh));
+
+	/* Note: We do the rest of the work on the PROCESS_SACK
+	 * sideeffect.
+	 */
+	return SCTP_DISPOSITION_CONSUME;
+}
+
+/*
+ * Generate an ABORT in response to a packet.
+ *
+ * Section: 8.4 Handle "Out of the blue" Packets
+ *
+ * 8) The receiver should respond to the sender of the OOTB packet
+ *    with an ABORT.  When sending the ABORT, the receiver of the
+ *    OOTB packet MUST fill in the Verification Tag field of the
+ *    outbound packet with the value found in the Verification Tag
+ *    field of the OOTB packet and set the T-bit in the Chunk Flags
+ *    to indicate that no TCB was found.  After sending this ABORT,
+ *    the receiver of the OOTB packet shall discard the OOTB packet
+ *    and take no further action.
+ *
+ * Verification Tag:
+ *
+ * The return value is the disposition of the chunk.
+*/
+sctp_disposition_t sctp_sf_tabort_8_4_8(const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					const sctp_subtype_t type,
+					void *arg,
+					sctp_cmd_seq_t *commands)
+{
+	struct sctp_packet *packet = NULL;
+	sctp_chunk_t *chunk = arg;
+	sctp_chunk_t *abort;
+
+	packet = sctp_ootb_pkt_new(asoc, chunk);
+
+	if (packet) {
+		/* Make an ABORT. The T bit will be set if the asoc
+		 * is NULL.
+		 */
+        	abort = sctp_make_abort(asoc, chunk, 0);
+		if (!abort) {
+			sctp_ootb_pkt_free(packet);
+			return SCTP_DISPOSITION_NOMEM;
+		}
+
+		/* Set the skb to the belonging sock for accounting.  */
+		abort->skb->sk = ep->base.sk;
+
+		sctp_packet_append_chunk(packet, abort);
+
+		sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
+				SCTP_PACKET(packet));
+
+		SCTP_INC_STATS(SctpOutCtrlChunks);
+
+		return SCTP_DISPOSITION_CONSUME;
+	}
+
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/*
+ * Received an ERROR chunk from peer.  Generate SCTP_REMOTE_ERROR
+ * event as ULP notification for each cause included in the chunk.
+ *
+ * API 5.3.1.3 - SCTP_REMOTE_ERROR
+ *
+ * The return value is the disposition of the chunk.
+*/
+sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					const sctp_subtype_t type,
+					void *arg,
+					sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+	struct sctp_ulpevent *ev;
+
+	while (chunk->chunk_end > chunk->skb->data) {
+		ev = sctp_ulpevent_make_remote_error(asoc, chunk, 0,
+						     GFP_ATOMIC);
+		if (!ev)
+			goto nomem;
+
+		if (!sctp_add_cmd(commands, SCTP_CMD_EVENT_ULP,
+				  SCTP_ULPEVENT(ev))) {
+			sctp_ulpevent_free(ev);
+			goto nomem;
+		}
+	}
+	return SCTP_DISPOSITION_CONSUME;
+
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/*
+ * Process an inbound SHUTDOWN ACK.
+ *
+ * From Section 9.2:
+ * Upon the receipt of the SHUTDOWN ACK, the SHUTDOWN sender shall
+ * stop the T2-shutdown timer, send a SHUTDOWN COMPLETE chunk to its
+ * peer, and remove all record of the association.
+ *
+ * The return value is the disposition.
+ */
+sctp_disposition_t sctp_sf_do_9_2_final(const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					const sctp_subtype_t type,
+					void *arg,
+					sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+	sctp_chunk_t *reply;
+	struct sctp_ulpevent *ev;
+
+	/* 10.2 H) SHUTDOWN COMPLETE notification
+	 *
+	 * When SCTP completes the shutdown procedures (section 9.2) this
+	 * notification is passed to the upper layer.
+	 */
+	ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_SHUTDOWN_COMP,
+					     0, 0, 0, GFP_ATOMIC);
+	if (!ev)
+		goto nomem;
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev));
+
+	/* Upon the receipt of the SHUTDOWN ACK, the SHUTDOWN sender shall
+	 * stop the T2-shutdown timer,
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN));
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD));
+
+	/* ...send a SHUTDOWN COMPLETE chunk to its peer, */
+	reply = sctp_make_shutdown_complete(asoc, chunk);
+	if (!reply)
+		goto nomem;
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+			SCTP_STATE(SCTP_STATE_CLOSED));
+	SCTP_INC_STATS(SctpShutdowns);
+	SCTP_DEC_STATS(SctpCurrEstab);
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
+
+	/* ...and remove all record of the association. */
+	sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
+	return SCTP_DISPOSITION_DELETE_TCB;
+
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/*
+ * RFC 2960, 8.4 - Handle "Out of the blue" Packets
+ * 5) If the packet contains a SHUTDOWN ACK chunk, the receiver should
+ *    respond to the sender of the OOTB packet with a SHUTDOWN COMPLETE.
+ *    When sending the SHUTDOWN COMPLETE, the receiver of the OOTB
+ *    packet must fill in the Verification Tag field of the outbound
+ *    packet with the Verification Tag received in the SHUTDOWN ACK and
+ *    set the T-bit in the Chunk Flags to indicate that no TCB was
+ *    found. Otherwise,
+ *
+ * 8) The receiver should respond to the sender of the OOTB packet with
+ *    an ABORT.  When sending the ABORT, the receiver of the OOTB packet
+ *    MUST fill in the Verification Tag field of the outbound packet
+ *    with the value found in the Verification Tag field of the OOTB
+ *    packet and set the T-bit in the Chunk Flags to indicate that no
+ *    TCB was found.  After sending this ABORT, the receiver of the OOTB
+ *    packet shall discard the OOTB packet and take no further action.
+ */
+sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep,
+				const struct sctp_association *asoc,
+				const sctp_subtype_t type,
+				void *arg,
+				sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+	struct sk_buff *skb = chunk->skb;
+	sctp_chunkhdr_t *ch;
+	__u8 *ch_end;
+	int ootb_shut_ack = 0;
+
+	SCTP_INC_STATS(SctpOutOfBlues);
+
+	ch = (sctp_chunkhdr_t *) chunk->chunk_hdr;
+	do {
+		ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length));
+
+		if (SCTP_CID_SHUTDOWN_ACK == ch->type)
+			ootb_shut_ack = 1;
+
+		ch = (sctp_chunkhdr_t *) ch_end;
+	} while (ch_end < skb->tail);
+
+	if (ootb_shut_ack)
+		sctp_sf_shut_8_4_5(ep, asoc, type, arg, commands);
+	else
+		sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands);
+
+	return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+}
+
+/*
+ * Handle an "Out of the blue" SHUTDOWN ACK.
+ *
+ * Section: 8.4 5)
+ * 5) If the packet contains a SHUTDOWN ACK chunk, the receiver should
+ *   respond to the sender of the OOTB packet with a SHUTDOWN COMPLETE.
+ *   When sending the SHUTDOWN COMPLETE, the receiver of the OOTB packet
+ *   must fill in the Verification Tag field of the outbound packet with
+ *   the Verification Tag received in the SHUTDOWN ACK and set the
+ *   T-bit in the Chunk Flags to indicate that no TCB was found.
+ *
+ * Inputs
+ * (endpoint, asoc, type, arg, commands)
+ *
+ * Outputs
+ * (sctp_disposition_t)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep,
+				      const struct sctp_association *asoc,
+				      const sctp_subtype_t type,
+				      void *arg,
+				      sctp_cmd_seq_t *commands)
+{
+	struct sctp_packet *packet = NULL;
+	sctp_chunk_t *chunk = arg;
+	sctp_chunk_t *shut;
+
+	packet = sctp_ootb_pkt_new(asoc, chunk);
+
+	if (packet) {
+		/* Make an SHUTDOWN_COMPLETE.
+         	 * The T bit will be set if the asoc is NULL.
+         	 */
+		shut = sctp_make_shutdown_complete(asoc, chunk);
+		if (!shut) {
+			sctp_ootb_pkt_free(packet);
+			return SCTP_DISPOSITION_NOMEM;
+		}
+
+		/* Set the skb to the belonging sock for accounting.  */
+		shut->skb->sk = ep->base.sk;
+
+		sctp_packet_append_chunk(packet, shut);
+
+		sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
+				SCTP_PACKET(packet));
+
+		SCTP_INC_STATS(SctpOutCtrlChunks);
+
+		return SCTP_DISPOSITION_CONSUME;
+	}
+
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/*
+ * Handle SHUTDOWN ACK in COOKIE_ECHOED or COOKIE_WAIT state.
+ *
+ * Verification Tag:  8.5.1 E) Rules for packet carrying a SHUTDOWN ACK
+ *   If the receiver is in COOKIE-ECHOED or COOKIE-WAIT state the
+ *   procedures in section 8.4 SHOULD be followed, in other words it
+ *   should be treated as an Out Of The Blue packet.
+ *   [This means that we do NOT check the Verification Tag on these
+ *   chunks. --piggy ]
+ *
+ */
+sctp_disposition_t sctp_sf_do_8_5_1_E_sa(const struct sctp_endpoint *ep,
+				      const struct sctp_association *asoc,
+				      const sctp_subtype_t type,
+				      void *arg,
+				      sctp_cmd_seq_t *commands)
+{
+	/* Although we do have an association in this case, it corresponds
+	 * to a restarted association. So the packet is treated as an OOTB
+	 * packet and the state function that handles OOTB SHUTDOWN_ACK is
+	 * called with a NULL association.
+	 */
+	return sctp_sf_shut_8_4_5(ep, NULL, type, arg, commands);
+}
+
+/*
+ * Process an unknown chunk.
+ *
+ * Section: 3.2. Also, 2.1 in the implementor's guide.
+ *
+ * Chunk Types are encoded such that the highest-order two bits specify
+ * the action that must be taken if the processing endpoint does not
+ * recognize the Chunk Type.
+ *
+ * 00 - Stop processing this SCTP packet and discard it, do not process
+ *      any further chunks within it.
+ *
+ * 01 - Stop processing this SCTP packet and discard it, do not process
+ *      any further chunks within it, and report the unrecognized
+ *      chunk in an 'Unrecognized Chunk Type'.
+ *
+ * 10 - Skip this chunk and continue processing.
+ *
+ * 11 - Skip this chunk and continue processing, but report in an ERROR
+ *      Chunk using the 'Unrecognized Chunk Type' cause of error.
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_unk_chunk(const struct sctp_endpoint *ep,
+				     const struct sctp_association *asoc,
+				     const sctp_subtype_t type,
+				     void *arg,
+				     sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *unk_chunk = arg;
+	sctp_chunk_t *err_chunk;
+	sctp_chunkhdr_t *hdr;
+
+	SCTP_DEBUG_PRINTK("Processing the unknown chunk id %d.\n", type.chunk);
+
+	/* 8.5 When receiving an SCTP packet, the endpoint MUST ensure
+	 * that the value in the Verification Tag field of the
+	 * received SCTP packet matches its own Tag.  If the received
+	 * Verification Tag value does not match the receiver's own
+	 * tag value, the receiver shall silently discard the packet.
+	 */
+	if (ntohl(unk_chunk->sctp_hdr->vtag) != asoc->c.my_vtag)
+		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+
+	switch (type.chunk & SCTP_CID_ACTION_MASK) {
+	case SCTP_CID_ACTION_DISCARD:
+		/* Discard the packet.  */
+		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+		break;
+	case SCTP_CID_ACTION_DISCARD_ERR:
+		/* Discard the packet.  */
+		sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+
+		/* Generate an ERROR chunk as response. */
+		hdr = unk_chunk->chunk_hdr;
+		err_chunk = sctp_make_op_error(asoc, unk_chunk,
+					       SCTP_ERROR_UNKNOWN_CHUNK, hdr,
+					       WORD_ROUND(ntohs(hdr->length)));
+		if (err_chunk) {
+			sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
+					SCTP_CHUNK(err_chunk));
+		}
+		return SCTP_DISPOSITION_CONSUME;
+		break;
+	case SCTP_CID_ACTION_SKIP:
+		/* Skip the chunk.  */
+		return SCTP_DISPOSITION_DISCARD;
+		break;
+	case SCTP_CID_ACTION_SKIP_ERR:
+		/* Generate an ERROR chunk as response. */
+		hdr = unk_chunk->chunk_hdr;
+		err_chunk = sctp_make_op_error(asoc, unk_chunk,
+					       SCTP_ERROR_UNKNOWN_CHUNK, hdr,
+					       WORD_ROUND(ntohs(hdr->length)));
+		if (err_chunk) {
+			sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
+					SCTP_CHUNK(err_chunk));
+		}
+		/* Skip the chunk.  */
+		return SCTP_DISPOSITION_CONSUME;
+		break;
+	default:
+		break;
+	}
+
+	return SCTP_DISPOSITION_DISCARD;
+}
+
+/*
+ * Discard the chunk.
+ *
+ * Section: 0.2, 5.2.3, 5.2.5, 5.2.6, 6.0, 8.4.6, 8.5.1c, 9.2
+ * [Too numerous to mention...]
+ * Verification Tag: No verification needed.
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_discard_chunk(const struct sctp_endpoint *ep,
+					 const struct sctp_association *asoc,
+					 const sctp_subtype_t type,
+					 void *arg,
+					 sctp_cmd_seq_t *commands)
+{
+	SCTP_DEBUG_PRINTK("Chunk %d is discarded\n", type.chunk);
+	return SCTP_DISPOSITION_DISCARD;
+}
+
+/*
+ * Discard the whole packet.
+ *
+ * Section: 8.4 2)
+ *
+ * 2) If the OOTB packet contains an ABORT chunk, the receiver MUST
+ *    silently discard the OOTB packet and take no further action.
+ *    Otherwise,
+ *
+ * Verification Tag: No verification necessary
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_pdiscard(const struct sctp_endpoint *ep,
+				    const struct sctp_association *asoc,
+				    const sctp_subtype_t type,
+				    void *arg,
+				    sctp_cmd_seq_t *commands)
+{
+	sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET, SCTP_NULL());
+
+	return SCTP_DISPOSITION_CONSUME;
+}
+
+#if 0
+/*
+ * We did something stupid but got lucky.  Namely, we sent a HEARTBEAT
+ * before the association was all the way up and we did NOT get an
+ * ABORT.
+ *
+ * Log the fact and then process normally.
+ *
+ * Section: Not specified
+ * Verification Tag:  8.5 Verification Tag [Normal verification]
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t lucky(const struct sctp_endpoint *ep,
+			 const struct sctp_association *asoc,
+			 const sctp_subtype_t type,
+			 void *arg,
+			 sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+
+	/* 8.5 When receiving an SCTP packet, the endpoint MUST ensure
+	 * that the value in the Verification Tag field of the
+	 * received SCTP packet matches its own Tag. ...
+	 */
+	if (chunk->sctp_hdr->vtag != asoc->c.my_vtag)
+		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+
+	return SCTP_DISPOSITION_CONSUME;
+
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+#endif /* 0 */
+
+#if 0
+/*
+ * The other end is doing something very stupid.  We'll ignore them
+ * after logging their idiocy. :-)
+ *
+ * Section: Not specified
+ * Verification Tag:  8.5 Verification Tag [Normal verification]
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t other_stupid(const struct sctp_endpoint *ep,
+				const struct sctp_association *asoc,
+				const sctp_subtype_t type,
+				void *arg,
+				sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+
+	/* 8.5 When receiving an SCTP packet, the endpoint MUST ensure
+	 * that the value in the Verification Tag field of the
+	 * received SCTP packet matches its own Tag. ...
+	 */
+	if (chunk->sctp_hdr->vtag != asoc->c.my_vtag)
+		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+
+	return SCTP_DISPOSITION_CONSUME;
+
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+#endif /* 0 */
+
+/*
+ * The other end is violating protocol.
+ *
+ * Section: Not specified
+ * Verification Tag: Not specified
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * We simply tag the chunk as a violation.  The state machine will log
+ * the violation and continue.
+ */
+sctp_disposition_t sctp_sf_violation(const struct sctp_endpoint *ep,
+				     const struct sctp_association *asoc,
+				     const sctp_subtype_t type,
+				     void *arg,
+				     sctp_cmd_seq_t *commands)
+{
+	return SCTP_DISPOSITION_VIOLATION;
+}
+
+/***************************************************************************
+ * These are the state functions for handling primitive (Section 10) events.
+ ***************************************************************************/
+/*
+ * sctp_sf_do_prm_asoc
+ *
+ * Section: 10.1 ULP-to-SCTP
+ * B) Associate
+ *
+ * Format: ASSOCIATE(local SCTP instance name, destination transport addr,
+ * outbound stream count)
+ * -> association id [,destination transport addr list] [,outbound stream
+ * count]
+ *
+ * This primitive allows the upper layer to initiate an association to a
+ * specific peer endpoint.
+ *
+ * The peer endpoint shall be specified by one of the transport addresses
+ * which defines the endpoint (see Section 1.4).  If the local SCTP
+ * instance has not been initialized, the ASSOCIATE is considered an
+ * error.
+ * [This is not relevant for the kernel implementation since we do all
+ * initialization at boot time.  It we hadn't initialized we wouldn't
+ * get anywhere near this code.]
+ *
+ * An association id, which is a local handle to the SCTP association,
+ * will be returned on successful establishment of the association. If
+ * SCTP is not able to open an SCTP association with the peer endpoint,
+ * an error is returned.
+ * [In the kernel implementation, the struct sctp_association needs to
+ * be created BEFORE causing this primitive to run.]
+ *
+ * Other association parameters may be returned, including the
+ * complete destination transport addresses of the peer as well as the
+ * outbound stream count of the local endpoint. One of the transport
+ * address from the returned destination addresses will be selected by
+ * the local endpoint as default primary path for sending SCTP packets
+ * to this peer.  The returned "destination transport addr list" can
+ * be used by the ULP to change the default primary path or to force
+ * sending a packet to a specific transport address.  [All of this
+ * stuff happens when the INIT ACK arrives.  This is a NON-BLOCKING
+ * function.]
+ *
+ * Mandatory attributes:
+ *
+ * o local SCTP instance name - obtained from the INITIALIZE operation.
+ *   [This is the argument asoc.]
+ * o destination transport addr - specified as one of the transport
+ * addresses of the peer endpoint with which the association is to be
+ * established.
+ *  [This is asoc->peer.active_path.]
+ * o outbound stream count - the number of outbound streams the ULP
+ * would like to open towards this peer endpoint.
+ * [BUG: This is not currently implemented.]
+ * Optional attributes:
+ *
+ * None.
+ *
+ * The return value is a disposition.
+ */
+sctp_disposition_t sctp_sf_do_prm_asoc(const struct sctp_endpoint *ep,
+				       const struct sctp_association *asoc,
+				       const sctp_subtype_t type,
+				       void *arg,
+				       sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *repl;
+
+	/* The comment below says that we enter COOKIE-WAIT AFTER
+	 * sending the INIT, but that doesn't actually work in our
+	 * implementation...
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+			SCTP_STATE(SCTP_STATE_COOKIE_WAIT));
+
+	/* RFC 2960 5.1 Normal Establishment of an Association
+	 *
+	 * A) "A" first sends an INIT chunk to "Z".  In the INIT, "A"
+	 * must provide its Verification Tag (Tag_A) in the Initiate
+	 * Tag field.  Tag_A SHOULD be a random number in the range of
+	 * 1 to 4294967295 (see 5.3.1 for Tag value selection). ...
+	 */
+
+	repl = sctp_make_init(asoc, &asoc->base.bind_addr, GFP_ATOMIC, 0);
+	if (!repl)
+		goto nomem;
+
+	/* Cast away the const modifier, as we want to just
+	 * rerun it through as a sideffect.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC,
+			SCTP_ASOC((struct sctp_association *) asoc));
+
+	/* After sending the INIT, "A" starts the T1-init timer and
+	 * enters the COOKIE-WAIT state.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
+	return SCTP_DISPOSITION_CONSUME;
+
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/*
+ * Process the SEND primitive.
+ *
+ * Section: 10.1 ULP-to-SCTP
+ * E) Send
+ *
+ * Format: SEND(association id, buffer address, byte count [,context]
+ *         [,stream id] [,life time] [,destination transport address]
+ *         [,unorder flag] [,no-bundle flag] [,payload protocol-id] )
+ * -> result
+ *
+ * This is the main method to send user data via SCTP.
+ *
+ * Mandatory attributes:
+ *
+ *  o association id - local handle to the SCTP association
+ *
+ *  o buffer address - the location where the user message to be
+ *    transmitted is stored;
+ *
+ *  o byte count - The size of the user data in number of bytes;
+ *
+ * Optional attributes:
+ *
+ *  o context - an optional 32 bit integer that will be carried in the
+ *    sending failure notification to the ULP if the transportation of
+ *    this User Message fails.
+ *
+ *  o stream id - to indicate which stream to send the data on. If not
+ *    specified, stream 0 will be used.
+ *
+ *  o life time - specifies the life time of the user data. The user data
+ *    will not be sent by SCTP after the life time expires. This
+ *    parameter can be used to avoid efforts to transmit stale
+ *    user messages. SCTP notifies the ULP if the data cannot be
+ *    initiated to transport (i.e. sent to the destination via SCTP's
+ *    send primitive) within the life time variable. However, the
+ *    user data will be transmitted if SCTP has attempted to transmit a
+ *    chunk before the life time expired.
+ *
+ *  o destination transport address - specified as one of the destination
+ *    transport addresses of the peer endpoint to which this packet
+ *    should be sent. Whenever possible, SCTP should use this destination
+ *    transport address for sending the packets, instead of the current
+ *    primary path.
+ *
+ *  o unorder flag - this flag, if present, indicates that the user
+ *    would like the data delivered in an unordered fashion to the peer
+ *    (i.e., the U flag is set to 1 on all DATA chunks carrying this
+ *    message).
+ *
+ *  o no-bundle flag - instructs SCTP not to bundle this user data with
+ *    other outbound DATA chunks. SCTP MAY still bundle even when
+ *    this flag is present, when faced with network congestion.
+ *
+ *  o payload protocol-id - A 32 bit unsigned integer that is to be
+ *    passed to the peer indicating the type of payload protocol data
+ *    being transmitted. This value is passed as opaque data by SCTP.
+ *
+ * The return value is the disposition.
+ */
+sctp_disposition_t sctp_sf_do_prm_send(const struct sctp_endpoint *ep,
+				       const struct sctp_association *asoc,
+				       const sctp_subtype_t type,
+				       void *arg,
+				       sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = arg;
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(chunk));
+	return SCTP_DISPOSITION_CONSUME;
+}
+
+/*
+ * Process the SHUTDOWN primitive.
+ *
+ * Section: 10.1:
+ * C) Shutdown
+ *
+ * Format: SHUTDOWN(association id)
+ * -> result
+ *
+ * Gracefully closes an association. Any locally queued user data
+ * will be delivered to the peer. The association will be terminated only
+ * after the peer acknowledges all the SCTP packets sent.  A success code
+ * will be returned on successful termination of the association. If
+ * attempting to terminate the association results in a failure, an error
+ * code shall be returned.
+ *
+ * Mandatory attributes:
+ *
+ *  o association id - local handle to the SCTP association
+ *
+ * Optional attributes:
+ *
+ * None.
+ *
+ * The return value is the disposition.
+ */
+sctp_disposition_t sctp_sf_do_9_2_prm_shutdown(
+	const struct sctp_endpoint *ep,
+	const struct sctp_association *asoc,
+	const sctp_subtype_t type,
+	void *arg,
+	sctp_cmd_seq_t *commands)
+{
+	int disposition;
+
+	/* From 9.2 Shutdown of an Association
+	 * Upon receipt of the SHUTDOWN primitive from its upper
+	 * layer, the endpoint enters SHUTDOWN-PENDING state and
+	 * remains there until all outstanding data has been
+	 * acknowledged by its peer. The endpoint accepts no new data
+	 * from its upper layer, but retransmits data to the far end
+	 * if necessary to fill gaps.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+			SCTP_STATE(SCTP_STATE_SHUTDOWN_PENDING));
+
+	/* sctpimpguide-05 Section 2.12.2
+	 * The sender of the SHUTDOWN MAY also start an overall guard timer
+	 * 'T5-shutdown-guard' to bound the overall time for shutdown sequence.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD));
+
+	disposition = SCTP_DISPOSITION_CONSUME;
+	if (sctp_outq_is_empty(&asoc->outqueue)) {
+		disposition = sctp_sf_do_9_2_start_shutdown(ep, asoc, type,
+							    arg, commands);
+	}
+	return disposition;
+}
+
+/*
+ * Process the ABORT primitive.
+ *
+ * Section: 10.1:
+ * C) Abort
+ *
+ * Format: Abort(association id [, cause code])
+ * -> result
+ *
+ * Ungracefully closes an association. Any locally queued user data
+ * will be discarded and an ABORT chunk is sent to the peer.  A success code
+ * will be returned on successful abortion of the association. If
+ * attempting to abort the association results in a failure, an error
+ * code shall be returned.
+ *
+ * Mandatory attributes:
+ *
+ *  o association id - local handle to the SCTP association
+ *
+ * Optional attributes:
+ *
+ *  o cause code - reason of the abort to be passed to the peer
+ *
+ * None.
+ *
+ * The return value is the disposition.
+ */
+sctp_disposition_t sctp_sf_do_9_1_prm_abort(
+	const struct sctp_endpoint *ep,
+	const struct sctp_association *asoc,
+	const sctp_subtype_t type,
+	void *arg,
+	sctp_cmd_seq_t *commands)
+{
+	/* From 9.1 Abort of an Association
+	 * Upon receipt of the ABORT primitive from its upper
+	 * layer, the endpoint enters CLOSED state and
+	 * discard all outstanding data has been
+	 * acknowledged by its peer. The endpoint accepts no new data
+	 * from its upper layer, but retransmits data to the far end
+	 * if necessary to fill gaps.
+	 */
+	struct msghdr *msg = arg;
+	sctp_chunk_t *abort;
+	sctp_disposition_t retval;
+
+	retval = SCTP_DISPOSITION_CONSUME;
+
+	/* Generate ABORT chunk to send the peer.  */
+	abort = sctp_make_abort_user(asoc, NULL, msg);
+	if (!abort)
+		retval = SCTP_DISPOSITION_NOMEM;
+	else
+		sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
+
+	/* Even if we can't send the ABORT due to low memory delete the
+	 * TCB.  This is a departure from our typical NOMEM handling.
+	 */
+
+	/* Delete the established association. */
+	sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
+			SCTP_U32(SCTP_ERROR_USER_ABORT));
+
+	SCTP_INC_STATS(SctpAborteds);
+	SCTP_DEC_STATS(SctpCurrEstab);
+
+	return retval;
+}
+
+/* We tried an illegal operation on an association which is closed.  */
+sctp_disposition_t sctp_sf_error_closed(const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					const sctp_subtype_t type,
+					void *arg,
+					sctp_cmd_seq_t *commands)
+{
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_ERROR, SCTP_ERROR(-EINVAL));
+	return SCTP_DISPOSITION_CONSUME;
+}
+
+/* We tried an illegal operation on an association which is shutting
+ * down.
+ */
+sctp_disposition_t sctp_sf_error_shutdown(const struct sctp_endpoint *ep,
+					  const struct sctp_association *asoc,
+					  const sctp_subtype_t type,
+					  void *arg,
+					  sctp_cmd_seq_t *commands)
+{
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_ERROR,
+			SCTP_ERROR(-ESHUTDOWN));
+	return SCTP_DISPOSITION_CONSUME;
+}
+
+/*
+ * sctp_cookie_wait_prm_shutdown
+ *
+ * Section: 4 Note: 2
+ * Verification Tag:
+ * Inputs
+ * (endpoint, asoc)
+ *
+ * The RFC does not explicitly address this issue, but is the route through the
+ * state table when someone issues a shutdown while in COOKIE_WAIT state.
+ *
+ * Outputs
+ * (timers)
+ */
+sctp_disposition_t sctp_sf_cookie_wait_prm_shutdown(
+	const struct sctp_endpoint *ep,
+	const struct sctp_association *asoc,
+	const sctp_subtype_t type,
+	void *arg,
+	sctp_cmd_seq_t *commands)
+{
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+			SCTP_STATE(SCTP_STATE_CLOSED));
+
+	SCTP_INC_STATS(SctpShutdowns);
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
+
+	return SCTP_DISPOSITION_DELETE_TCB;
+}
+
+/*
+ * sctp_cookie_echoed_prm_shutdown
+ *
+ * Section: 4 Note: 2
+ * Verification Tag:
+ * Inputs
+ * (endpoint, asoc)
+ *
+ * The RFC does not explcitly address this issue, but is the route through the
+ * state table when someone issues a shutdown while in COOKIE_ECHOED state.
+ *
+ * Outputs
+ * (timers)
+ */
+sctp_disposition_t sctp_sf_cookie_echoed_prm_shutdown(
+	const struct sctp_endpoint *ep,
+	const struct sctp_association *asoc,
+	const sctp_subtype_t type,
+	void *arg, sctp_cmd_seq_t *commands)
+{
+	/* There is a single T1 timer, so we should be able to use
+	 * common function with the COOKIE-WAIT state.
+	 */
+	return sctp_sf_cookie_wait_prm_shutdown(ep, asoc, type, arg, commands);
+}
+
+/*
+ * sctp_sf_cookie_wait_prm_abort
+ *
+ * Section: 4 Note: 2
+ * Verification Tag:
+ * Inputs
+ * (endpoint, asoc)
+ *
+ * The RFC does not explicitly address this issue, but is the route through the
+ * state table when someone issues an abort while in COOKIE_WAIT state.
+ *
+ * Outputs
+ * (timers)
+ */
+sctp_disposition_t sctp_sf_cookie_wait_prm_abort(
+	const struct sctp_endpoint *ep,
+	const struct sctp_association *asoc,
+	const sctp_subtype_t type,
+	void *arg,
+	sctp_cmd_seq_t *commands)
+{
+	struct msghdr *msg = arg;
+	sctp_chunk_t *abort;
+	sctp_disposition_t retval;
+
+	/* Stop T1-init timer */
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
+	retval = SCTP_DISPOSITION_CONSUME;
+
+	/* Generate ABORT chunk to send the peer */
+	abort = sctp_make_abort_user(asoc, NULL, msg);
+	if (!abort)
+		retval = SCTP_DISPOSITION_NOMEM;
+	else
+		sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+			SCTP_STATE(SCTP_STATE_CLOSED));
+
+	SCTP_INC_STATS(SctpAborteds);
+
+	/* Even if we can't send the ABORT due to low memory delete the
+	 * TCB.  This is a departure from our typical NOMEM handling.
+	 */
+
+	/* Delete the established association. */
+	sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
+			SCTP_U32(SCTP_ERROR_USER_ABORT));
+
+	return retval;
+}
+
+/*
+ * sctp_sf_cookie_echoed_prm_abort
+ *
+ * Section: 4 Note: 3
+ * Verification Tag:
+ * Inputs
+ * (endpoint, asoc)
+ *
+ * The RFC does not explcitly address this issue, but is the route through the
+ * state table when someone issues an abort while in COOKIE_ECHOED state.
+ *
+ * Outputs
+ * (timers)
+ */
+sctp_disposition_t sctp_sf_cookie_echoed_prm_abort(
+	const struct sctp_endpoint *ep,
+	const struct sctp_association *asoc,
+	const sctp_subtype_t type,
+	void *arg,
+	sctp_cmd_seq_t *commands)
+{
+	/* There is a single T1 timer, so we should be able to use
+	 * common function with the COOKIE-WAIT state.
+	 */
+	return sctp_sf_cookie_wait_prm_abort(ep, asoc, type, arg, commands);
+}
+
+/*
+ * sctp_sf_shutdown_pending_prm_abort
+ *
+ * Inputs
+ * (endpoint, asoc)
+ *
+ * The RFC does not explicitly address this issue, but is the route through the
+ * state table when someone issues an abort while in SHUTDOWN-PENDING state.
+ *
+ * Outputs
+ * (timers)
+ */
+sctp_disposition_t sctp_sf_shutdown_pending_prm_abort(
+	const struct sctp_endpoint *ep,
+	const struct sctp_association *asoc,
+	const sctp_subtype_t type,
+	void *arg,
+	sctp_cmd_seq_t *commands)
+{
+	/* Stop the T5-shutdown guard timer.  */
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD));
+
+	return sctp_sf_do_9_1_prm_abort(ep, asoc, type, arg, commands);
+}
+
+/*
+ * sctp_sf_shutdown_sent_prm_abort
+ *
+ * Inputs
+ * (endpoint, asoc)
+ *
+ * The RFC does not explicitly address this issue, but is the route through the
+ * state table when someone issues an abort while in SHUTDOWN-SENT state.
+ *
+ * Outputs
+ * (timers)
+ */
+sctp_disposition_t sctp_sf_shutdown_sent_prm_abort(
+	const struct sctp_endpoint *ep,
+	const struct sctp_association *asoc,
+	const sctp_subtype_t type,
+	void *arg,
+	sctp_cmd_seq_t *commands)
+{
+	/* Stop the T2-shutdown timer.  */
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN));
+
+	/* Stop the T5-shutdown guard timer.  */
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD));
+
+	return sctp_sf_do_9_1_prm_abort(ep, asoc, type, arg, commands);
+}
+
+/*
+ * sctp_sf_cookie_echoed_prm_abort
+ *
+ * Inputs
+ * (endpoint, asoc)
+ *
+ * The RFC does not explcitly address this issue, but is the route through the
+ * state table when someone issues an abort while in COOKIE_ECHOED state.
+ *
+ * Outputs
+ * (timers)
+ */
+sctp_disposition_t sctp_sf_shutdown_ack_sent_prm_abort(
+	const struct sctp_endpoint *ep,
+	const struct sctp_association *asoc,
+	const sctp_subtype_t type,
+	void *arg,
+	sctp_cmd_seq_t *commands)
+{
+	/* The same T2 timer, so we should be able to use
+	 * common function with the SHUTDOWN-SENT state.
+	 */
+	return sctp_sf_shutdown_sent_prm_abort(ep, asoc, type, arg, commands);
+}
+
+/*
+ * Process the REQUESTHEARTBEAT primitive
+ *
+ * 10.1 ULP-to-SCTP
+ * J) Request Heartbeat
+ *
+ * Format: REQUESTHEARTBEAT(association id, destination transport address)
+ *
+ * -> result
+ *
+ * Instructs the local endpoint to perform a HeartBeat on the specified
+ * destination transport address of the given association. The returned
+ * result should indicate whether the transmission of the HEARTBEAT
+ * chunk to the destination address is successful.
+ *
+ * Mandatory attributes:
+ *
+ * o association id - local handle to the SCTP association
+ *
+ * o destination transport address - the transport address of the
+ *   association on which a heartbeat should be issued.
+ */
+sctp_disposition_t sctp_sf_do_prm_requestheartbeat(
+					const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					const sctp_subtype_t type,
+					void *arg,
+					sctp_cmd_seq_t *commands)
+{
+	return sctp_sf_heartbeat(ep, asoc, type, (struct sctp_transport *)arg,
+				 commands);
+}
+
+/*
+ * Ignore the primitive event
+ *
+ * The return value is the disposition of the primitive.
+ */
+sctp_disposition_t sctp_sf_ignore_primitive(
+	const struct sctp_endpoint *ep,
+	const struct sctp_association *asoc,
+	const sctp_subtype_t type,
+	void *arg,
+	sctp_cmd_seq_t *commands)
+{
+	SCTP_DEBUG_PRINTK("Primitive type %d is ignored.\n", type.primitive);
+	return SCTP_DISPOSITION_DISCARD;
+}
+
+/***************************************************************************
+ * These are the state functions for the OTHER events.
+ ***************************************************************************/
+
+/*
+ * Start the shutdown negotiation.
+ *
+ * From Section 9.2:
+ * Once all its outstanding data has been acknowledged, the endpoint
+ * shall send a SHUTDOWN chunk to its peer including in the Cumulative
+ * TSN Ack field the last sequential TSN it has received from the peer.
+ * It shall then start the T2-shutdown timer and enter the SHUTDOWN-SENT
+ * state. If the timer expires, the endpoint must re-send the SHUTDOWN
+ * with the updated last sequential TSN received from its peer.
+ *
+ * The return value is the disposition.
+ */
+sctp_disposition_t sctp_sf_do_9_2_start_shutdown(
+	const struct sctp_endpoint *ep,
+	const struct sctp_association *asoc,
+	const sctp_subtype_t type,
+	void *arg,
+	sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *reply;
+
+	/* Once all its outstanding data has been acknowledged, the
+	 * endpoint shall send a SHUTDOWN chunk to its peer including
+	 * in the Cumulative TSN Ack field the last sequential TSN it
+	 * has received from the peer.
+	 */
+	reply = sctp_make_shutdown(asoc);
+	if (!reply)
+		goto nomem;
+
+	/* Set the transport for the SHUTDOWN chunk and the timeout for the
+	 * T2-shutdown timer.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_SETUP_T2, SCTP_CHUNK(reply));
+
+	/* It shall then start the T2-shutdown timer */
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN));
+
+	if (asoc->autoclose)
+		sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+				SCTP_TO(SCTP_EVENT_TIMEOUT_AUTOCLOSE));
+
+	/* and enter the SHUTDOWN-SENT state.  */
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+			SCTP_STATE(SCTP_STATE_SHUTDOWN_SENT));
+
+	/* sctp-implguide 2.10 Issues with Heartbeating and failover
+	 *
+	 * HEARTBEAT ... is discontinued after sending either SHUTDOWN
+         * or SHUTDOWN-ACK.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_STOP, SCTP_NULL());
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
+
+	return SCTP_DISPOSITION_CONSUME;
+
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/*
+ * Generate a SHUTDOWN ACK now that everything is SACK'd.
+ *
+ * From Section 9.2:
+ *
+ * If it has no more outstanding DATA chunks, the SHUTDOWN receiver
+ * shall send a SHUTDOWN ACK and start a T2-shutdown timer of its own,
+ * entering the SHUTDOWN-ACK-SENT state. If the timer expires, the
+ * endpoint must re-send the SHUTDOWN ACK.
+ *
+ * The return value is the disposition.
+ */
+sctp_disposition_t sctp_sf_do_9_2_shutdown_ack(
+	const struct sctp_endpoint *ep,
+	const struct sctp_association *asoc,
+	const sctp_subtype_t type,
+	void *arg,
+	sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *chunk = (sctp_chunk_t *) arg;
+	sctp_chunk_t *reply;
+
+	/* If it has no more outstanding DATA chunks, the SHUTDOWN receiver
+	 * shall send a SHUTDOWN ACK ...
+	 */
+	reply = sctp_make_shutdown_ack(asoc, chunk);
+	if (!reply)
+		goto nomem;
+
+	/* Set the transport for the SHUTDOWN ACK chunk and the timeout for
+	 * the T2-shutdown timer.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_SETUP_T2, SCTP_CHUNK(reply));
+
+	/* and start/restart a T2-shutdown timer of its own, */
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN));
+
+	if (asoc->autoclose)
+		sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+				SCTP_TO(SCTP_EVENT_TIMEOUT_AUTOCLOSE));
+
+	/* Enter the SHUTDOWN-ACK-SENT state.  */
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+			SCTP_STATE(SCTP_STATE_SHUTDOWN_ACK_SENT));
+
+	/* sctp-implguide 2.10 Issues with Heartbeating and failover
+	 *
+	 * HEARTBEAT ... is discontinued after sending either SHUTDOWN
+         * or SHUTDOWN-ACK.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_STOP, SCTP_NULL());
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
+
+	return SCTP_DISPOSITION_CONSUME;
+
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/*
+ * Ignore the event defined as other
+ *
+ * The return value is the disposition of the event.
+ */
+sctp_disposition_t sctp_sf_ignore_other(const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					const sctp_subtype_t type,
+					void *arg,
+					sctp_cmd_seq_t *commands)
+{
+	SCTP_DEBUG_PRINTK("The event other type %d is ignored\n", type.other);
+	return SCTP_DISPOSITION_DISCARD;
+}
+
+/************************************************************
+ * These are the state functions for handling timeout events.
+ ************************************************************/
+
+/*
+ * RTX Timeout
+ *
+ * Section: 6.3.3 Handle T3-rtx Expiration
+ *
+ * Whenever the retransmission timer T3-rtx expires for a destination
+ * address, do the following:
+ * [See below]
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_do_6_3_3_rtx(const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					const sctp_subtype_t type,
+					void *arg,
+					sctp_cmd_seq_t *commands)
+{
+	struct sctp_transport *transport = arg;
+
+	if (asoc->overall_error_count >= asoc->overall_error_threshold) {
+		/* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
+		sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
+				SCTP_U32(SCTP_ERROR_NO_ERROR));
+		SCTP_INC_STATS(SctpAborteds);
+		SCTP_DEC_STATS(SctpCurrEstab);
+		return SCTP_DISPOSITION_DELETE_TCB;
+	}
+
+	/* E1) For the destination address for which the timer
+	 * expires, adjust its ssthresh with rules defined in Section
+	 * 7.2.3 and set the cwnd <- MTU.
+	 */
+
+	/* E2) For the destination address for which the timer
+	 * expires, set RTO <- RTO * 2 ("back off the timer").  The
+	 * maximum value discussed in rule C7 above (RTO.max) may be
+	 * used to provide an upper bound to this doubling operation.
+	 */
+
+	/* E3) Determine how many of the earliest (i.e., lowest TSN)
+	 * outstanding DATA chunks for the address for which the
+	 * T3-rtx has expired will fit into a single packet, subject
+	 * to the MTU constraint for the path corresponding to the
+	 * destination transport address to which the retransmission
+	 * is being sent (this may be different from the address for
+	 * which the timer expires [see Section 6.4]).  Call this
+	 * value K. Bundle and retransmit those K DATA chunks in a
+	 * single packet to the destination endpoint.
+	 *
+	 * Note: Any DATA chunks that were sent to the address for
+	 * which the T3-rtx timer expired but did not fit in one MTU
+	 * (rule E3 above), should be marked for retransmission and
+	 * sent as soon as cwnd allows (normally when a SACK arrives).
+	 */
+
+	/* NB: Rules E4 and F1 are implicit in R1.  */
+	sctp_add_cmd_sf(commands, SCTP_CMD_RETRAN, SCTP_TRANSPORT(transport));
+
+	/* Do some failure management (Section 8.2). */
+	sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE, SCTP_TRANSPORT(transport));
+
+	return SCTP_DISPOSITION_CONSUME;
+}
+
+/*
+ * Generate delayed SACK on timeout
+ *
+ * Section: 6.2  Acknowledgement on Reception of DATA Chunks
+ *
+ * The guidelines on delayed acknowledgement algorithm specified in
+ * Section 4.2 of [RFC2581] SHOULD be followed.  Specifically, an
+ * acknowledgement SHOULD be generated for at least every second packet
+ * (not every second DATA chunk) received, and SHOULD be generated
+ * within 200 ms of the arrival of any unacknowledged DATA chunk.  In
+ * some situations it may be beneficial for an SCTP transmitter to be
+ * more conservative than the algorithms detailed in this document
+ * allow. However, an SCTP transmitter MUST NOT be more aggressive than
+ * the following algorithms allow.
+ */
+sctp_disposition_t sctp_sf_do_6_2_sack(const struct sctp_endpoint *ep,
+				       const struct sctp_association *asoc,
+				       const sctp_subtype_t type,
+				       void *arg,
+				       sctp_cmd_seq_t *commands)
+{
+	sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_FORCE());
+	return SCTP_DISPOSITION_CONSUME;
+}
+
+/*
+ * sctp_sf_t1_timer_expire
+ *
+ * Section: 4 Note: 2
+ * Verification Tag:
+ * Inputs
+ * (endpoint, asoc)
+ *
+ *  RFC 2960 Section 4 Notes
+ *  2) If the T1-init timer expires, the endpoint MUST retransmit INIT
+ *     and re-start the T1-init timer without changing state.  This MUST
+ *     be repeated up to 'Max.Init.Retransmits' times.  After that, the
+ *     endpoint MUST abort the initialization process and report the
+ *     error to SCTP user.
+ *
+ *   3) If the T1-cookie timer expires, the endpoint MUST retransmit
+ *     COOKIE ECHO and re-start the T1-cookie timer without changing
+ *     state.  This MUST be repeated up to 'Max.Init.Retransmits' times.
+ *     After that, the endpoint MUST abort the initialization process and
+ *     report the error to SCTP user.
+ *
+ * Outputs
+ * (timers, events)
+ *
+ */
+sctp_disposition_t sctp_sf_t1_timer_expire(const struct sctp_endpoint *ep,
+					   const struct sctp_association *asoc,
+					   const sctp_subtype_t type,
+					   void *arg,
+					   sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *repl;
+	sctp_bind_addr_t *bp;
+	sctp_event_timeout_t timer = (sctp_event_timeout_t) arg;
+	int timeout;
+	int attempts;
+
+	timeout = asoc->timeouts[timer];
+	attempts = asoc->counters[SCTP_COUNTER_INIT_ERROR] + 1;
+	repl = NULL;
+
+	SCTP_DEBUG_PRINTK("Timer T1 expired.\n");
+
+	if ((timeout < asoc->max_init_timeo) &&
+	    (attempts < asoc->max_init_attempts)) {
+		switch (timer) {
+		case SCTP_EVENT_TIMEOUT_T1_INIT:
+			bp = (sctp_bind_addr_t *) &asoc->base.bind_addr;
+			repl = sctp_make_init(asoc, bp, GFP_ATOMIC, 0);
+			break;
+
+		case SCTP_EVENT_TIMEOUT_T1_COOKIE:
+			repl = sctp_make_cookie_echo(asoc, NULL);
+			break;
+
+		default:
+			BUG();
+			break;
+		};
+
+		if (!repl)
+			goto nomem;
+
+		/* Issue a sideeffect to do the needed accounting. */
+		sctp_add_cmd_sf(commands, SCTP_CMD_INIT_RESTART,
+				SCTP_TO(timer));
+		sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
+	} else {
+		sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
+				SCTP_U32(SCTP_ERROR_NO_ERROR));
+		return SCTP_DISPOSITION_DELETE_TCB;
+	}
+
+	return SCTP_DISPOSITION_CONSUME;
+
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/* RFC2960 9.2 If the timer expires, the endpoint must re-send the SHUTDOWN
+ * with the updated last sequential TSN received from its peer.
+ *
+ * An endpoint should limit the number of retransmissions of the
+ * SHUTDOWN chunk to the protocol parameter 'Association.Max.Retrans'.
+ * If this threshold is exceeded the endpoint should destroy the TCB and
+ * MUST report the peer endpoint unreachable to the upper layer (and
+ * thus the association enters the CLOSED state).  The reception of any
+ * packet from its peer (i.e. as the peer sends all of its queued DATA
+ * chunks) should clear the endpoint's retransmission count and restart
+ * the T2-Shutdown timer,  giving its peer ample opportunity to transmit
+ * all of its queued DATA chunks that have not yet been sent.
+ */
+sctp_disposition_t sctp_sf_t2_timer_expire(const struct sctp_endpoint *ep,
+					   const struct sctp_association *asoc,
+					   const sctp_subtype_t type,
+					   void *arg,
+					   sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *reply = NULL;
+
+	SCTP_DEBUG_PRINTK("Timer T2 expired.\n");
+	if (asoc->overall_error_count >= asoc->overall_error_threshold) {
+		/* Note:  CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
+		sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
+				SCTP_U32(SCTP_ERROR_NO_ERROR));
+		SCTP_INC_STATS(SctpAborteds);
+		SCTP_DEC_STATS(SctpCurrEstab);
+		return SCTP_DISPOSITION_DELETE_TCB;
+	}
+
+	switch (asoc->state) {
+	case SCTP_STATE_SHUTDOWN_SENT:
+		reply = sctp_make_shutdown(asoc);
+		break;
+
+	case SCTP_STATE_SHUTDOWN_ACK_SENT:
+		reply = sctp_make_shutdown_ack(asoc, NULL);
+		break;
+
+	default:
+		BUG();
+		break;
+	};
+
+	if (!reply)
+		goto nomem;
+
+	/* Do some failure management (Section 8.2). */
+	sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE,
+			SCTP_TRANSPORT(asoc->shutdown_last_sent_to));
+
+	/* Set the transport for the SHUTDOWN/ACK chunk and the timeout for
+	 * the T2-shutdown timer.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_SETUP_T2, SCTP_CHUNK(reply));
+
+	/* Restart the T2-shutdown timer.  */
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN));
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
+	return SCTP_DISPOSITION_CONSUME;
+
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/* sctpimpguide-05 Section 2.12.2
+ * The sender of the SHUTDOWN MAY also start an overall guard timer
+ * 'T5-shutdown-guard' to bound the overall time for shutdown sequence.
+ * At the expiration of this timer the sender SHOULD abort the association
+ * by sending an ABORT chunk.
+ */
+sctp_disposition_t sctp_sf_t5_timer_expire(const struct sctp_endpoint *ep,
+					   const struct sctp_association *asoc,
+					   const sctp_subtype_t type,
+					   void *arg,
+					   sctp_cmd_seq_t *commands)
+{
+	sctp_chunk_t *reply = NULL;
+
+	SCTP_DEBUG_PRINTK("Timer T5 expired.\n");
+
+	reply = sctp_make_abort(asoc, NULL, 0);
+	if (!reply)
+		goto nomem;
+
+	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
+	sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
+			SCTP_U32(SCTP_ERROR_NO_ERROR));
+
+	return SCTP_DISPOSITION_DELETE_TCB;
+nomem:
+	return SCTP_DISPOSITION_NOMEM;
+}
+
+/* Handle expiration of AUTOCLOSE timer.  When the autoclose timer expires,
+ * the association is automatically closed by starting the shutdown process.
+ * The work that needs to be done is same as when SHUTDOWN is initiated by
+ * the user.  So this routine looks same as sctp_sf_do_9_2_prm_shutdown().
+ */
+sctp_disposition_t sctp_sf_autoclose_timer_expire(
+	const struct sctp_endpoint *ep,
+	const struct sctp_association *asoc,
+	const sctp_subtype_t type,
+	void *arg,
+	sctp_cmd_seq_t *commands)
+{
+	int disposition;
+
+	/* From 9.2 Shutdown of an Association
+	 * Upon receipt of the SHUTDOWN primitive from its upper
+	 * layer, the endpoint enters SHUTDOWN-PENDING state and
+	 * remains there until all outstanding data has been
+	 * acknowledged by its peer. The endpoint accepts no new data
+	 * from its upper layer, but retransmits data to the far end
+	 * if necessary to fill gaps.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+			SCTP_STATE(SCTP_STATE_SHUTDOWN_PENDING));
+
+	/* sctpimpguide-05 Section 2.12.2
+	 * The sender of the SHUTDOWN MAY also start an overall guard timer
+	 * 'T5-shutdown-guard' to bound the overall time for shutdown sequence.
+ 	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START,
+			SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD));
+	disposition = SCTP_DISPOSITION_CONSUME;
+	if (sctp_outq_is_empty(&asoc->outqueue)) {
+		disposition = sctp_sf_do_9_2_start_shutdown(ep, asoc, type,
+							    arg, commands);
+	}
+	return disposition;
+}
+
+/*****************************************************************************
+ * These are sa state functions which could apply to all types of events.
+ ****************************************************************************/
+
+/*
+ * This table entry is not implemented.
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_not_impl(const struct sctp_endpoint *ep,
+				    const struct sctp_association *asoc,
+				    const sctp_subtype_t type,
+				    void *arg,
+				    sctp_cmd_seq_t *commands)
+{
+	return SCTP_DISPOSITION_NOT_IMPL;
+}
+
+/*
+ * This table entry represents a bug.
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_bug(const struct sctp_endpoint *ep,
+			       const struct sctp_association *asoc,
+			       const sctp_subtype_t type,
+			       void *arg,
+			       sctp_cmd_seq_t *commands)
+{
+	return SCTP_DISPOSITION_BUG;
+}
+
+/*
+ * This table entry represents the firing of a timer in the wrong state.
+ * Since timer deletion cannot be guaranteed a timer 'may' end up firing
+ * when the association is in the wrong state.   This event should
+ * be ignored, so as to prevent any rearming of the timer.
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_timer_ignore(const struct sctp_endpoint *ep,
+					const struct sctp_association *asoc,
+					const sctp_subtype_t type,
+					void *arg,
+					sctp_cmd_seq_t *commands)
+{
+	SCTP_DEBUG_PRINTK("Timer %d ignored.\n", type.chunk);
+	return SCTP_DISPOSITION_CONSUME;
+}
+
+/********************************************************************
+ * 2nd Level Abstractions
+ ********************************************************************/
+
+/* Pull the SACK chunk based on the SACK header. */
+sctp_sackhdr_t *sctp_sm_pull_sack(sctp_chunk_t *chunk)
+{
+	sctp_sackhdr_t *sack;
+	__u16 num_blocks;
+	__u16 num_dup_tsns;
+
+	/* FIXME:  Protect ourselves from reading too far into
+	 * the skb from a bogus sender.
+	 */
+	sack = (sctp_sackhdr_t *) chunk->skb->data;
+	skb_pull(chunk->skb, sizeof(sctp_sackhdr_t));
+
+	num_blocks = ntohs(sack->num_gap_ack_blocks);
+	num_dup_tsns = ntohs(sack->num_dup_tsns);
+
+	skb_pull(chunk->skb, (num_blocks + num_dup_tsns) * sizeof(__u32));
+	return sack;
+}
+
+/* Create an ABORT packet to be sent as a response, with the specified
+ * error causes.
+ */
+struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *ep,
+				  const struct sctp_association *asoc,
+				  sctp_chunk_t *chunk,
+				  const void *payload,
+				  size_t paylen)
+{
+	struct sctp_packet *packet;
+	sctp_chunk_t *abort;
+
+	packet = sctp_ootb_pkt_new(asoc, chunk);
+
+	if (packet) {
+		/* Make an ABORT.
+		 * The T bit will be set if the asoc is NULL.
+		 */
+		abort = sctp_make_abort(asoc, chunk, paylen);
+		if (!abort) {
+			sctp_ootb_pkt_free(packet);
+			return NULL;
+		}
+		/* Add specified error causes, i.e., payload, to the
+		 * end of the chunk.
+		 */
+		sctp_addto_chunk(abort, paylen, payload);
+
+		/* Set the skb to the belonging sock for accounting.  */
+		abort->skb->sk = ep->base.sk;
+
+		sctp_packet_append_chunk(packet, abort);
+
+	}
+
+	return packet;
+}
+
+/* Allocate a packet for responding in the OOTB conditions.  */
+struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *asoc,
+				 const sctp_chunk_t *chunk)
+{
+	struct sctp_packet *packet;
+	struct sctp_transport *transport;
+	__u16 sport;
+	__u16 dport;
+	__u32 vtag;
+
+	/* Get the source and destination port from the inbound packet.  */
+	sport = ntohs(chunk->sctp_hdr->dest);
+	dport = ntohs(chunk->sctp_hdr->source);
+
+	/* The V-tag is going to be the same as the inbound packet if no
+	 * association exists, otherwise, use the peer's vtag.
+	 */
+	if (asoc) {
+		vtag = asoc->peer.i.init_tag;
+	} else {
+		/* Special case the INIT as there is no vtag yet. */
+		if (SCTP_CID_INIT == chunk->chunk_hdr->type) {
+			sctp_init_chunk_t *init;
+			init = (sctp_init_chunk_t *)chunk->chunk_hdr;
+			vtag = ntohl(init->init_hdr.init_tag);
+		} else {
+			vtag = ntohl(chunk->sctp_hdr->vtag);
+		}
+	}
+
+	/* Make a transport for the bucket, Eliza... */
+	transport = sctp_transport_new(sctp_source(chunk), GFP_ATOMIC);
+
+	if (!transport)
+		goto nomem;
+
+	/* Allocate a new packet for sending the response. */
+	packet = t_new(struct sctp_packet, GFP_ATOMIC);
+	if (!packet)
+		goto nomem_packet;
+
+	/* Cache a route for the transport with the chunk's destination as
+	 * the source address.
+	 */
+	sctp_transport_route(transport, (union sctp_addr *)&chunk->dest,
+			     sctp_sk(sctp_get_ctl_sock()));
+
+	packet = sctp_packet_init(packet, transport, sport, dport);
+	packet = sctp_packet_config(packet, vtag, 0, NULL);
+
+	return packet;
+
+nomem_packet:
+	sctp_transport_free(transport);
+nomem:
+	return NULL;
+}
+
+/* Free the packet allocated earlier for responding in the OOTB condition.  */
+void sctp_ootb_pkt_free(struct sctp_packet *packet)
+{
+	sctp_transport_free(packet->transport);
+	sctp_packet_free(packet);
+}
+
+/* Send a stale cookie error when a invalid COOKIE ECHO chunk is found  */
+void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep,
+				const struct sctp_association *asoc,
+				const sctp_chunk_t *chunk,
+				sctp_cmd_seq_t *commands,
+				sctp_chunk_t *err_chunk)
+{
+	struct sctp_packet *packet;
+
+	if (err_chunk) {
+		packet = sctp_ootb_pkt_new(asoc, chunk);
+		if (packet) {
+			/* Set the skb to the belonging sock for accounting. */
+			err_chunk->skb->sk = ep->base.sk;
+			sctp_packet_append_chunk(packet, err_chunk);
+			sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
+					SCTP_PACKET(packet));
+			SCTP_INC_STATS(SctpOutCtrlChunks);
+		} else
+			sctp_free_chunk (err_chunk);
+	}
+}
diff -urN linux-2.4.23-pre5/net/sctp/sm_statetable.c linux-2.4.23-pre6/net/sctp/sm_statetable.c
--- linux-2.4.23-pre5/net/sctp/sm_statetable.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/sm_statetable.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,889 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001-2003 International Business Machines, Corp.
+ * Copyright (c) 2001 Intel Corp.
+ * Copyright (c) 2001 Nokia, Inc.
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * These are the state tables for the SCTP state machine.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Karl Knutson          <karl@athena.chicago.il.us>
+ *    Jon Grimm             <jgrimm@us.ibm.com>
+ *    Hui Huang		    <hui.huang@nokia.com>
+ *    Daisy Chang	    <daisyc@us.ibm.com>
+ *    Ardelle Fan	    <ardelle.fan@intel.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/skbuff.h>
+#include <net/sctp/sctp.h>
+#include <net/sctp/sm.h>
+
+static sctp_sm_table_entry_t bug = {
+	.fn = sctp_sf_bug, 
+	.name = "sctp_sf_bug"
+};
+
+#define DO_LOOKUP(_max, _type, _table) \
+	if ((event_subtype._type > (_max))) { \
+		printk(KERN_WARNING \
+		       "sctp table %p possible attack:" \
+		       " event %d exceeds max %d\n", \
+		       _table, event_subtype._type, _max); \
+		return &bug; \
+	} \
+	return &_table[event_subtype._type][(int)state];
+
+sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type,
+					    sctp_state_t state,
+					    sctp_subtype_t event_subtype)
+{
+	switch (event_type) {
+	case SCTP_EVENT_T_CHUNK:
+		return sctp_chunk_event_lookup(event_subtype.chunk, state);
+		break;
+	case SCTP_EVENT_T_TIMEOUT:
+		DO_LOOKUP(SCTP_EVENT_TIMEOUT_MAX, timeout, 
+			  timeout_event_table);
+		break;
+
+	case SCTP_EVENT_T_OTHER:
+		DO_LOOKUP(SCTP_EVENT_OTHER_MAX, other, other_event_table);
+		break;
+
+	case SCTP_EVENT_T_PRIMITIVE:
+		DO_LOOKUP(SCTP_EVENT_PRIMITIVE_MAX, primitive,
+			  primitive_event_table);
+		break;
+
+	default:
+		/* Yikes!  We got an illegal event type.  */
+		return &bug;
+	};
+}
+
+#define TYPE_SCTP_DATA { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_tabort_8_4_8, .name = "sctp_sf_tabort_8_4_8"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_eat_data_6_2, .name = "sctp_sf_eat_data_6_2"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_eat_data_6_2, .name = "sctp_sf_eat_data_6_2"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_eat_data_fast_4_4, .name = "sctp_sf_eat_data_fast_4_4"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+} /* TYPE_SCTP_DATA */
+
+#define TYPE_SCTP_INIT { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_do_5_1B_init, .name = "sctp_sf_do_5_1B_init"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_do_5_2_1_siminit, .name = "sctp_sf_do_5_2_1_siminit"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_do_5_2_1_siminit, .name = "sctp_sf_do_5_2_1_siminit"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_do_5_2_2_dupinit, .name = "sctp_sf_do_5_2_2_dupinit"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_do_5_2_2_dupinit, .name = "sctp_sf_do_5_2_2_dupinit"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_do_5_2_2_dupinit, .name = "sctp_sf_do_5_2_2_dupinit"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_do_5_2_2_dupinit, .name = "sctp_sf_do_5_2_2_dupinit"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_do_9_2_reshutack, .name = "sctp_sf_do_9_2_reshutack"}, \
+} /* TYPE_SCTP_INIT */
+
+#define TYPE_SCTP_INIT_ACK { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_do_5_1C_ack, .name = "sctp_sf_do_5_1C_ack"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+} /* TYPE_SCTP_INIT_ACK */
+
+#define TYPE_SCTP_SACK { \
+	/*  SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_tabort_8_4_8, .name = "sctp_sf_tabort_8_4_8"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_eat_sack_6_2, .name = "sctp_sf_eat_sack_6_2"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_eat_sack_6_2, .name = "sctp_sf_eat_sack_6_2"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_eat_sack_6_2, .name = "sctp_sf_eat_sack_6_2"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_eat_sack_6_2, .name = "sctp_sf_eat_sack_6_2"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+} /* TYPE_SCTP_SACK */
+
+#define TYPE_SCTP_HEARTBEAT { \
+	/*  SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_tabort_8_4_8, .name = "sctp_sf_tabort_8_4_8"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_beat_8_3, .name = "sctp_sf_beat_8_3"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_beat_8_3, .name = "sctp_sf_beat_8_3"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_beat_8_3, .name = "sctp_sf_beat_8_3"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_beat_8_3, .name = "sctp_sf_beat_8_3"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_beat_8_3, .name = "sctp_sf_beat_8_3"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	/* This should not happen, but we are nice.  */ \
+	{.fn = sctp_sf_beat_8_3, .name = "sctp_sf_beat_8_3"}, \
+} /* TYPE_SCTP_HEARTBEAT */
+
+#define TYPE_SCTP_HEARTBEAT_ACK { \
+	/*  SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_tabort_8_4_8, .name = "sctp_sf_tabort_8_4_8"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_violation, .name = "sctp_sf_violation"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_backbeat_8_3, .name = "sctp_sf_backbeat_8_3"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_backbeat_8_3, .name = "sctp_sf_backbeat_8_3"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_backbeat_8_3, .name = "sctp_sf_backbeat_8_3"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_backbeat_8_3, .name = "sctp_sf_backbeat_8_3"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+} /* TYPE_SCTP_HEARTBEAT_ACK */
+
+#define TYPE_SCTP_ABORT { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_pdiscard, .name = "sctp_sf_pdiscard"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_cookie_wait_abort, .name = "sctp_sf_cookie_wait_abort"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_cookie_echoed_abort, \
+	 .name = "sctp_sf_cookie_echoed_abort"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_do_9_1_abort, .name = "sctp_sf_do_9_1_abort"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_shutdown_pending_abort, \
+	.name = "sctp_sf_shutdown_pending_abort"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_shutdown_sent_abort, \
+	.name = "sctp_sf_shutdown_sent_abort"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_do_9_1_abort, .name = "sctp_sf_do_9_1_abort"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_shutdown_ack_sent_abort, \
+	.name = "sctp_sf_shutdown_ack_sent_abort"}, \
+} /* TYPE_SCTP_ABORT */
+
+#define TYPE_SCTP_SHUTDOWN { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_tabort_8_4_8, .name = "sctp_sf_tabort_8_4_8"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_do_9_2_shutdown, .name = "sctp_sf_do_9_2_shutdown"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_do_9_2_shutdown_ack, \
+	 .name = "sctp_sf_do_9_2_shutdown_ack"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+} /* TYPE_SCTP_SHUTDOWN */
+
+#define TYPE_SCTP_SHUTDOWN_ACK { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_do_8_5_1_E_sa, .name = "sctp_sf_do_8_5_1_E_sa"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_do_8_5_1_E_sa, .name = "sctp_sf_do_8_5_1_E_sa"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_violation, .name = "sctp_sf_violation"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_violation, .name = "sctp_sf_violation"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_do_9_2_final, .name = "sctp_sf_do_9_2_final"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_violation, .name = "sctp_sf_violation"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_do_9_2_final, .name = "sctp_sf_do_9_2_final"}, \
+} /* TYPE_SCTP_SHUTDOWN_ACK */
+
+#define TYPE_SCTP_ERROR { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_tabort_8_4_8, .name = "sctp_sf_tabort_8_4_8"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_cookie_echoed_err, .name = "sctp_sf_cookie_echoed_err"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_operr_notify, .name = "sctp_sf_operr_notify"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_operr_notify, .name = "sctp_sf_operr_notify"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_operr_notify, .name = "sctp_sf_operr_notify"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+} /* TYPE_SCTP_ERROR */
+
+#define TYPE_SCTP_COOKIE_ECHO { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_do_5_1D_ce, .name = "sctp_sf_do_5_1D_ce"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_do_5_2_4_dupcook, .name = "sctp_sf_do_5_2_4_dupcook"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_do_5_2_4_dupcook, .name = "sctp_sf_do_5_2_4_dupcook"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_do_5_2_4_dupcook, .name = "sctp_sf_do_5_2_4_dupcook"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_do_5_2_4_dupcook, .name = "sctp_sf_do_5_2_4_dupcook"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_do_5_2_4_dupcook, .name = "sctp_sf_do_5_2_4_dupcook"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_do_5_2_4_dupcook, .name = "sctp_sf_do_5_2_4_dupcook"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_do_5_2_4_dupcook, .name = "sctp_sf_do_5_2_4_dupcook"}, \
+} /* TYPE_SCTP_COOKIE_ECHO */
+
+#define TYPE_SCTP_COOKIE_ACK { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_do_5_1E_ca, .name = "sctp_sf_do_5_1E_ca"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+} /* TYPE_SCTP_COOKIE_ACK */
+
+#define TYPE_SCTP_ECN_ECNE { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_do_ecne, .name = "sctp_sf_do_ecne"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_do_ecne, .name = "sctp_sf_do_ecne"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_do_ecne, .name = "sctp_sf_do_ecne"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_do_ecne, .name = "sctp_sf_do_ecne"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_do_ecne, .name = "sctp_sf_do_ecne"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+} /* TYPE_SCTP_ECN_ECNE */
+
+#define TYPE_SCTP_ECN_CWR { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_do_ecn_cwr, .name = "sctp_sf_do_ecn_cwr"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_do_ecn_cwr, .name = "sctp_sf_do_ecn_cwr"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_do_ecn_cwr, .name = "sctp_sf_do_ecn_cwr"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+} /* TYPE_SCTP_ECN_CWR */
+
+#define TYPE_SCTP_SHUTDOWN_COMPLETE { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_do_4_C, .name = "sctp_sf_do_4_C"}, \
+} /* TYPE_SCTP_SHUTDOWN_COMPLETE */
+
+/* The primary index for this table is the chunk type.
+ * The secondary index for this table is the state.
+ *
+ * For base protocol (RFC 2960).
+ */
+sctp_sm_table_entry_t chunk_event_table[SCTP_NUM_BASE_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = {
+	TYPE_SCTP_DATA,
+	TYPE_SCTP_INIT,
+	TYPE_SCTP_INIT_ACK,
+	TYPE_SCTP_SACK,
+	TYPE_SCTP_HEARTBEAT,
+	TYPE_SCTP_HEARTBEAT_ACK,
+	TYPE_SCTP_ABORT,
+	TYPE_SCTP_SHUTDOWN,
+	TYPE_SCTP_SHUTDOWN_ACK,
+	TYPE_SCTP_ERROR,
+	TYPE_SCTP_COOKIE_ECHO,
+	TYPE_SCTP_COOKIE_ACK,
+	TYPE_SCTP_ECN_ECNE,
+	TYPE_SCTP_ECN_CWR,
+	TYPE_SCTP_SHUTDOWN_COMPLETE,
+}; /* state_fn_t chunk_event_table[][] */
+
+static sctp_sm_table_entry_t
+chunk_event_table_asconf[SCTP_STATE_NUM_STATES] = {
+	/* SCTP_STATE_EMPTY */
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
+	/* SCTP_STATE_CLOSED */
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
+	/* SCTP_STATE_COOKIE_WAIT */
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
+	/* SCTP_STATE_COOKIE_ECHOED */
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
+	/* SCTP_STATE_ESTABLISHED */
+	{.fn = sctp_sf_discard_chunk,
+	 .name = "sctp_sf_discard_chunk (will be sctp_addip_do_asconf)"},
+	/* SCTP_STATE_SHUTDOWN_PENDING */
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
+	/* SCTP_STATE_SHUTDOWN_SENT */
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
+};	/* chunk asconf */
+
+static sctp_sm_table_entry_t
+chunk_event_table_asconf_ack[SCTP_STATE_NUM_STATES] = {
+	/* SCTP_STATE_EMPTY */
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
+	/* SCTP_STATE_CLOSED */
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
+	/* SCTP_STATE_COOKIE_WAIT */
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
+	/* SCTP_STATE_COOKIE_ECHOED */
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
+	/* SCTP_STATE_ESTABLISHED */
+	{.fn = sctp_sf_discard_chunk,
+	 .name = "sctp_sf_discard_chunk (will be sctp_addip_do_asconf_ack)"},
+	/* SCTP_STATE_SHUTDOWN_PENDING */
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
+	/* SCTP_STATE_SHUTDOWN_SENT */
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */
+	{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
+};	/* chunk asconf_ack */
+
+static sctp_sm_table_entry_t
+chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = {
+	/* SCTP_STATE_EMPTY */
+	{.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"},
+	/* SCTP_STATE_CLOSED */
+	{.fn = sctp_sf_tabort_8_4_8, .name = "sctp_sf_tabort_8_4_8"},
+	/* SCTP_STATE_COOKIE_WAIT */
+	{.fn = sctp_sf_unk_chunk, .name = "sctp_sf_unk_chunk"},
+	/* SCTP_STATE_COOKIE_ECHOED */
+	{.fn = sctp_sf_unk_chunk, .name = "sctp_sf_unk_chunk"},
+	/* SCTP_STATE_ESTABLISHED */
+	{.fn = sctp_sf_unk_chunk, .name = "sctp_sf_unk_chunk"},
+	/* SCTP_STATE_SHUTDOWN_PENDING */
+	{.fn = sctp_sf_unk_chunk, .name = "sctp_sf_unk_chunk"},
+	/* SCTP_STATE_SHUTDOWN_SENT */
+	{.fn = sctp_sf_unk_chunk, .name = "sctp_sf_unk_chunk"},
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */
+	{.fn = sctp_sf_unk_chunk, .name = "sctp_sf_unk_chunk"},
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */
+	{.fn = sctp_sf_unk_chunk, .name = "sctp_sf_unk_chunk"},
+};	/* chunk unknown */
+
+
+#define TYPE_SCTP_PRIMITIVE_ASSOCIATE  { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_do_prm_asoc, .name = "sctp_sf_do_prm_asoc"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
+} /* TYPE_SCTP_PRIMITIVE_ASSOCIATE */
+
+#define TYPE_SCTP_PRIMITIVE_SHUTDOWN  { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_error_closed, .name = "sctp_sf_error_closed"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_cookie_wait_prm_shutdown, \
+	 .name = "sctp_sf_cookie_wait_prm_shutdown"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_cookie_echoed_prm_shutdown, \
+	 .name = "sctp_sf_cookie_echoed_prm_shutdown"},\
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_do_9_2_prm_shutdown, \
+	 .name = "sctp_sf_do_9_2_prm_shutdown"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_ignore_primitive, .name = "sctp_sf_ignore_primitive"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_ignore_primitive, .name = "sctp_sf_ignore_primitive"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_ignore_primitive, .name = "sctp_sf_ignore_primitive"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_ignore_primitive, .name = "sctp_sf_ignore_primitive"}, \
+} /* TYPE_SCTP_PRIMITIVE_SHUTDOWN */
+
+#define TYPE_SCTP_PRIMITIVE_ABORT  { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_error_closed, .name = "sctp_sf_error_closed"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_cookie_wait_prm_abort, \
+	.name = "sctp_sf_cookie_wait_prm_abort"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_cookie_echoed_prm_abort, \
+	.name = "sctp_sf_cookie_echoed_prm_abort"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_do_9_1_prm_abort, \
+	.name = "sctp_sf_do_9_1_prm_abort"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_shutdown_pending_prm_abort, \
+	.name = "sctp_sf_shutdown_pending_prm_abort"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_shutdown_sent_prm_abort, \
+	.name = "sctp_sf_shutdown_sent_prm_abort"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_do_9_1_prm_abort, \
+	.name = "sctp_sf_do_9_1_prm_abort"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_shutdown_ack_sent_prm_abort, \
+	.name = "sctp_sf_shutdown_ack_sent_prm_abort"}, \
+} /* TYPE_SCTP_PRIMITIVE_ABORT */
+
+#define TYPE_SCTP_PRIMITIVE_SEND  { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_error_closed, .name = "sctp_sf_error_closed"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_do_prm_send, .name = "sctp_sf_do_prm_send"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_do_prm_send, .name = "sctp_sf_do_prm_send"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_do_prm_send, .name = "sctp_sf_do_prm_send"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_error_shutdown, .name = "sctp_sf_error_shutdown"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_error_shutdown, .name = "sctp_sf_error_shutdown"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_error_shutdown, .name = "sctp_sf_error_shutdown"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_error_shutdown, .name = "sctp_sf_error_shutdown"}, \
+} /* TYPE_SCTP_PRIMITIVE_SEND */
+
+#define TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT  { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_do_prm_requestheartbeat,		      \
+	 .name = "sctp_sf_do_prm_requestheartbeat"},          \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_do_prm_requestheartbeat,		      \
+	 .name = "sctp_sf_do_prm_requestheartbeat"},          \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_do_prm_requestheartbeat,		      \
+	 .name = "sctp_sf_do_prm_requestheartbeat"},          \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_do_prm_requestheartbeat,		      \
+	 .name = "sctp_sf_do_prm_requestheartbeat"},          \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_do_prm_requestheartbeat,		      \
+	 .name = "sctp_sf_do_prm_requestheartbeat"},          \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_do_prm_requestheartbeat,		      \
+	 .name = "sctp_sf_do_prm_requestheartbeat"},          \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_do_prm_requestheartbeat,		      \
+	 .name = "sctp_sf_do_prm_requestheartbeat"},          \
+} /* TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT */
+
+
+/* The primary index for this table is the primitive type.
+ * The secondary index for this table is the state.
+ */
+sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP_STATE_NUM_STATES] = {
+	TYPE_SCTP_PRIMITIVE_ASSOCIATE,
+	TYPE_SCTP_PRIMITIVE_SHUTDOWN,
+	TYPE_SCTP_PRIMITIVE_ABORT,
+	TYPE_SCTP_PRIMITIVE_SEND,
+	TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT,
+};
+
+#define TYPE_SCTP_OTHER_NO_PENDING_TSN  { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_do_9_2_start_shutdown, \
+	 .name = "sctp_do_9_2_start_shutdown"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_do_9_2_shutdown_ack, \
+	 .name = "sctp_sf_do_9_2_shutdown_ack"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \
+}
+
+sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES] = {
+	TYPE_SCTP_OTHER_NO_PENDING_TSN,
+};
+
+#define TYPE_SCTP_EVENT_TIMEOUT_NONE { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+}
+
+#define TYPE_SCTP_EVENT_TIMEOUT_T1_COOKIE { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_t1_timer_expire, .name = "sctp_sf_t1_timer_expire"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+}
+
+#define TYPE_SCTP_EVENT_TIMEOUT_T1_INIT { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_t1_timer_expire, .name = "sctp_sf_t1_timer_expire"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+}
+
+#define TYPE_SCTP_EVENT_TIMEOUT_T2_SHUTDOWN { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_t2_timer_expire, .name = "sctp_sf_t2_timer_expire"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_t2_timer_expire, .name = "sctp_sf_t2_timer_expire"}, \
+}
+
+#define TYPE_SCTP_EVENT_TIMEOUT_T3_RTX { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_do_6_3_3_rtx, .name = "sctp_sf_do_6_3_3_rtx"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_do_6_3_3_rtx, .name = "sctp_sf_do_6_3_3_rtx"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_do_6_3_3_rtx, .name = "sctp_sf_do_6_3_3_rtx"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_do_6_3_3_rtx, .name = "sctp_sf_do_6_3_3_rtx"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+}
+
+#define TYPE_SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_t5_timer_expire, .name = "sctp_sf_t5_timer_expire"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+}
+
+#define TYPE_SCTP_EVENT_TIMEOUT_HEARTBEAT { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_sendbeat_8_3, .name = "sctp_sf_sendbeat_8_3"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_sendbeat_8_3, .name = "sctp_sf_sendbeat_8_3"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_sendbeat_8_3, .name = "sctp_sf_sendbeat_8_3"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+}
+
+#define TYPE_SCTP_EVENT_TIMEOUT_SACK { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_do_6_2_sack, .name = "sctp_sf_do_6_2_sack"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_do_6_2_sack, .name = "sctp_sf_do_6_2_sack"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_do_6_2_sack, .name = "sctp_sf_do_6_2_sack"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+}
+
+#define TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE { \
+	/* SCTP_STATE_EMPTY */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_CLOSED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_COOKIE_WAIT */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_COOKIE_ECHOED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_ESTABLISHED */ \
+	{.fn = sctp_sf_autoclose_timer_expire, \
+	 .name = "sctp_sf_autoclose_timer_expire"}, \
+	/* SCTP_STATE_SHUTDOWN_PENDING */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_SENT */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
+	{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+}
+
+sctp_sm_table_entry_t timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES] = {
+	TYPE_SCTP_EVENT_TIMEOUT_NONE,
+	TYPE_SCTP_EVENT_TIMEOUT_T1_COOKIE,
+	TYPE_SCTP_EVENT_TIMEOUT_T1_INIT,
+	TYPE_SCTP_EVENT_TIMEOUT_T2_SHUTDOWN,
+	TYPE_SCTP_EVENT_TIMEOUT_T3_RTX,
+	TYPE_SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD,
+	TYPE_SCTP_EVENT_TIMEOUT_HEARTBEAT,
+	TYPE_SCTP_EVENT_TIMEOUT_SACK,
+	TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE,
+};
+
+sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid, sctp_state_t state)
+{
+	if (state > SCTP_STATE_MAX)
+		return &bug;
+
+	if (cid >= 0 && cid <= SCTP_CID_BASE_MAX) {
+		return &chunk_event_table[cid][state];
+	}
+
+	switch (cid) {
+	case SCTP_CID_ASCONF:
+		return &chunk_event_table_asconf[state];
+
+	case SCTP_CID_ASCONF_ACK:
+		return &chunk_event_table_asconf_ack[state];
+	default:
+		return &chunk_event_table_unknown[state];
+	}
+}
diff -urN linux-2.4.23-pre5/net/sctp/socket.c linux-2.4.23-pre6/net/sctp/socket.c
--- linux-2.4.23-pre5/net/sctp/socket.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/socket.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,3639 @@
+/* Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001-2003 International Business Machines, Corp.
+ * Copyright (c) 2001-2003 Intel Corp.
+ * Copyright (c) 2001-2002 Nokia, Inc.
+ * Copyright (c) 2001 La Monte H.P. Yarroll
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * These functions interface with the sockets layer to implement the
+ * SCTP Extensions for the Sockets API.
+ *
+ * Note that the descriptions from the specification are USER level
+ * functions--this file is the functions which populate the struct proto
+ * for SCTP which is the BOTTOM of the sockets interface.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Narasimha Budihal     <narsi@refcode.org>
+ *    Karl Knutson          <karl@athena.chicago.il.us>
+ *    Jon Grimm             <jgrimm@us.ibm.com>
+ *    Xingang Guo           <xingang.guo@intel.com>
+ *    Daisy Chang           <daisyc@us.ibm.com>
+ *    Sridhar Samudrala     <samudrala@us.ibm.com>
+ *    Inaky Perez-Gonzalez  <inaky.gonzalez@intel.com>
+ *    Ardelle Fan	    <ardelle.fan@intel.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/ip.h>
+#include <linux/fcntl.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <net/route.h>
+#include <net/ipv6.h>
+#include <net/inet_common.h>
+
+#include <linux/socket.h> /* for sa_family_t */
+#include <net/sock.h>
+#include <net/sctp/sctp.h>
+
+/* WARNING:  Please do not remove the SCTP_STATIC attribute to
+ * any of the functions below as they are used to export functions
+ * used by a project regression testsuite.
+ */
+
+/* Forward declarations for internal helper functions. */
+static int sctp_writeable(struct sock *sk);
+static inline int sctp_wspace(struct sctp_association *asoc);
+static inline void sctp_set_owner_w(sctp_chunk_t *chunk);
+static void sctp_wfree(struct sk_buff *skb);
+static int sctp_wait_for_sndbuf(struct sctp_association *, long *timeo_p,
+				int msg_len);
+static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p);
+static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);
+static int sctp_wait_for_accept(struct sock *sk, long timeo);
+static inline int sctp_verify_addr(struct sock *, union sctp_addr *, int);
+static int sctp_bindx_add(struct sock *, struct sockaddr_storage *, int);
+static int sctp_bindx_rem(struct sock *, struct sockaddr_storage *, int);
+static int sctp_do_bind(struct sock *, union sctp_addr *, int);
+static int sctp_autobind(struct sock *sk);
+static void sctp_sock_migrate(struct sock *, struct sock *,
+			      struct sctp_association *, sctp_socket_type_t);
+static char *sctp_hmac_alg = SCTP_COOKIE_HMAC_ALG;
+
+/* Look up the association by its id.  If this is not a UDP-style
+ * socket, the ID field is always ignored.
+ */
+struct sctp_association *sctp_id2assoc(struct sock *sk, sctp_assoc_t id)
+{
+	struct sctp_association *asoc = NULL;
+
+	/* If this is not a UDP-style socket, assoc id should be
+	 * ignored.
+	 */
+	if (SCTP_SOCKET_UDP != sctp_sk(sk)->type) {
+		if (!list_empty(&sctp_sk(sk)->ep->asocs))
+			asoc = list_entry(sctp_sk(sk)->ep->asocs.next,
+					  struct sctp_association, asocs);
+		return asoc;
+	}
+
+	/* First, verify that this is a kernel address. */
+	if (sctp_is_valid_kaddr((unsigned long) id)) {
+		struct sctp_association *temp;
+
+		/* Verify that this _is_ an sctp_association
+		 * data structure and if so, that the socket matches.
+		 */
+		temp = (struct sctp_association *)id;
+		if ((SCTP_ASSOC_EYECATCHER == temp->eyecatcher) &&
+		    (temp->base.sk == sk))
+			asoc = temp;
+	}
+
+	return asoc;
+}
+
+/* API 3.1.2 bind() - UDP Style Syntax
+ * The syntax of bind() is,
+ *
+ *   ret = bind(int sd, struct sockaddr *addr, int addrlen);
+ *
+ *   sd      - the socket descriptor returned by socket().
+ *   addr    - the address structure (struct sockaddr_in or struct
+ *             sockaddr_in6 [RFC 2553]),
+ *   addrlen - the size of the address structure.
+ *
+ * The caller should use struct sockaddr_storage described in RFC 2553
+ * to represent addr for portability reason.
+ */
+int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+{
+	int retval = 0;
+
+	sctp_lock_sock(sk);
+
+	SCTP_DEBUG_PRINTK("sctp_bind(sk: %p, uaddr: %p, addr_len: %d)\n",
+			  sk, uaddr, addr_len);
+
+	/* Disallow binding twice. */
+	if (!sctp_sk(sk)->ep->base.bind_addr.port)
+		retval = sctp_do_bind(sk, (union sctp_addr *)uaddr,
+				      addr_len);
+	else
+		retval = -EINVAL;
+
+	sctp_release_sock(sk);
+
+	return retval;
+}
+
+static long sctp_get_port_local(struct sock *, union sctp_addr *);
+
+/* Verify this is a valid sockaddr. */
+static struct sctp_af *sctp_sockaddr_af(struct sctp_opt *opt,
+					union sctp_addr *addr, int len)
+{
+	struct sctp_af *af;
+
+	/* Check minimum size.  */
+	if (len < sizeof (struct sockaddr))
+		return NULL;
+
+	/* Does this PF support this AF? */
+	if (!opt->pf->af_supported(addr->sa.sa_family))
+		return NULL;
+
+	/* If we get this far, af is valid. */
+	af = sctp_get_af_specific(addr->sa.sa_family);
+
+	if (len < af->sockaddr_len)
+		return NULL;
+
+	return af;
+}
+
+/* Bind a local address either to an endpoint or to an association.  */
+SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
+{
+	struct sctp_opt *sp = sctp_sk(sk);
+	struct sctp_endpoint *ep = sp->ep;
+	sctp_bind_addr_t *bp = &ep->base.bind_addr;
+	struct sctp_af *af;
+	unsigned short snum;
+	int ret = 0;
+
+	SCTP_DEBUG_PRINTK("sctp_do_bind(sk: %p, newaddr: %p, len: %d)\n",
+			  sk, addr, len);
+
+	/* Common sockaddr verification. */
+	af = sctp_sockaddr_af(sp, addr, len);
+	if (!af)
+		return -EINVAL;
+
+	/* PF specific bind() address verification. */
+	if (!sp->pf->bind_verify(sp, addr))
+		return -EADDRNOTAVAIL;
+
+	snum= ntohs(addr->v4.sin_port);
+
+	SCTP_DEBUG_PRINTK("sctp_do_bind: port: %d, new port: %d\n",
+			  bp->port, snum);
+
+	/* We must either be unbound, or bind to the same port.  */
+	if (bp->port && (snum != bp->port)) {
+		SCTP_DEBUG_PRINTK("sctp_do_bind:"
+				  " New port %d does not match existing port "
+				  "%d.\n", snum, bp->port);
+		return -EINVAL;
+	}
+
+	if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
+		return -EACCES;
+
+	/* Make sure we are allowed to bind here.
+	 * The function sctp_get_port_local() does duplicate address
+	 * detection.
+	 */
+	if ((ret = sctp_get_port_local(sk, addr))) {
+		if (ret == (long) sk) {
+			/* This endpoint has a conflicting address. */
+			return -EINVAL;
+		} else {
+			return -EADDRINUSE;
+		}
+	}
+
+	/* Refresh ephemeral port.  */
+	if (!snum)
+		snum = (sk)->num;
+
+	/* Add the address to the bind address list.  */
+	sctp_local_bh_disable();
+	sctp_write_lock(&ep->base.addr_lock);
+
+	/* Use GFP_ATOMIC since BHs are disabled.  */
+	addr->v4.sin_port = ntohs(addr->v4.sin_port);
+	ret = sctp_add_bind_addr(bp, addr, GFP_ATOMIC);
+	addr->v4.sin_port = htons(addr->v4.sin_port);
+	if (!ret && !bp->port)
+		bp->port = snum;
+	sctp_write_unlock(&ep->base.addr_lock);
+	sctp_local_bh_enable();
+
+	/* Copy back into socket for getsockname() use. */
+	if (!ret) {
+		(sk)->sport = htons((sk)->num);
+		af->to_sk_saddr(addr, sk);
+	}
+
+	return ret;
+}
+
+/* API 8.1 sctp_bindx()
+ *
+ * The syntax of sctp_bindx() is,
+ *
+ *   ret = sctp_bindx(int sd,
+ *                    struct sockaddr_storage *addrs,
+ * 		      int addrcnt,
+ * 		      int flags);
+ *
+ * If sd is an IPv4 socket, the addresses passed must be IPv4 addresses.
+ * If the sd is an IPv6 socket, the addresses passed can either be IPv4
+ * or IPv6 addresses.
+ *
+ * A single address may be specified as INADDR_ANY or IPV6_ADDR_ANY, see
+ * section 3.1.2 for this usage.
+ *
+ * addrs is a pointer to an array of one or more socket addresses.  Each
+ * address is contained in a struct sockaddr_storage, so each address is
+ * fixed length. The caller specifies the number of addresses in the
+ * array with addrcnt.
+ *
+ * On success, sctp_bindx() returns 0. On failure, sctp_bindx() returns -1,
+ * and sets errno to the appropriate error code. [ Editor's note: need
+ * to fill in all error code? ]
+ *
+ * For SCTP, the port given in each socket address must be the same, or
+ * sctp_bindx() will fail, setting errno to EINVAL .
+ *
+ * The flags parameter is formed from the bitwise OR of zero or
+ * more of the following currently defined flags:
+ *
+ *     SCTP_BINDX_ADD_ADDR
+ *     SCTP_BINDX_REM_ADDR
+ *
+ * SCTP_BIND_ADD_ADDR directs SCTP to add the given addresses to the
+ * association, and SCTP_BIND_REM_ADDR directs SCTP to remove the given
+ * addresses from the association. The two flags are mutually exclusive;
+ * if both are given, sctp_bindx() will fail with EINVAL.  A caller may not
+ * remove all addresses from an association; sctp_bindx() will reject such
+ * an attempt with EINVAL.
+ *
+ * An application can use sctp_bindx(SCTP_BINDX_ADD_ADDR) to associate
+ * additional addresses with an endpoint after calling bind().  Or use
+ * sctp_bindx(SCTP_BINDX_REM_ADDR) to remove some addresses a listening
+ * socket is associated with so that no new association accepted will be
+ * associated with those addresses.
+ *
+ * SCTP_BIND_ADD_ADDR is defined as 0, so that it becomes the default
+ * behavior for sctp_bindx() when no flags are given.
+ *
+ * Adding and removing addresses from a connected association is optional
+ * functionality. Implementations that do not support this functionality
+ * should return EOPNOTSUPP.
+ *
+ * NOTE: This could be integrated into sctp_setsockopt_bindx(),
+ * but keeping it this way makes it easier if sometime sys_bindx is
+ * added.
+ */
+
+/* Unprotected by locks. Call only with socket lock sk->lock held! See
+ * sctp_bindx() for a lock-protected call.
+ */
+
+static int __sctp_bindx(struct sock *sk, struct sockaddr_storage *addrs,
+			int addrcnt, int flags)
+{
+	int retval = 0;
+
+	SCTP_DEBUG_PRINTK("__sctp_bindx(sk: %p, addrs: %p, addrcnt: %d, "
+			  "flags: %s)\n", sk, addrs, addrcnt,
+			  (BINDX_ADD_ADDR == flags) ? "ADD" :
+			  ((BINDX_REM_ADDR == flags) ? "REM" : "BOGUS"));
+
+	switch (flags) {
+	case BINDX_ADD_ADDR:
+		retval = sctp_bindx_add(sk, addrs, addrcnt);
+		break;
+
+	case BINDX_REM_ADDR:
+		retval = sctp_bindx_rem(sk, addrs, addrcnt);
+		break;
+
+	default:
+		retval = -EINVAL;
+		break;
+        };
+
+	return retval;
+}
+
+/* BINDX with locks.
+ *
+ * NOTE: Currently unused at all ...
+ */
+int sctp_bindx(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt,
+	       int flags)
+{
+	int retval;
+
+	sctp_lock_sock(sk);
+	retval = __sctp_bindx(sk, addrs, addrcnt, flags);
+	sctp_release_sock(sk);
+
+	return retval;
+}
+
+/* Add a list of addresses as bind addresses to local endpoint or
+ * association.
+ *
+ * Basically run through each address specified in the addrs/addrcnt
+ * array/length pair, determine if it is IPv6 or IPv4 and call
+ * sctp_do_bind() on it.
+ *
+ * If any of them fails, then the operation will be reversed and the
+ * ones that were added will be removed.
+ *
+ * Only __sctp_bindx() is supposed to call this function.
+ */
+int sctp_bindx_add(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt)
+{
+	int cnt;
+	int retval = 0;
+	int addr_len;
+
+	SCTP_DEBUG_PRINTK("sctp_bindx_add (sk: %p, addrs: %p, addrcnt: %d)\n",
+			  sk, addrs, addrcnt);
+
+	for (cnt = 0; cnt < addrcnt; cnt++) {
+		/* The list may contain either IPv4 or IPv6 address;
+		 * determine the address length for walking thru the list.
+		 */
+		switch (((struct sockaddr *)&addrs[cnt])->sa_family) {
+		case AF_INET:
+			addr_len = sizeof(struct sockaddr_in);
+			break;
+
+		case AF_INET6:
+			addr_len = sizeof(struct sockaddr_in6);
+			break;
+
+		default:
+			retval = -EINVAL;
+			goto err_bindx_add;
+		};
+
+		retval = sctp_do_bind(sk, (union sctp_addr *)&addrs[cnt],
+				      addr_len);
+
+err_bindx_add:
+		if (retval < 0) {
+			/* Failed. Cleanup the ones that has been added */
+			if (cnt > 0)
+				sctp_bindx_rem(sk, addrs, cnt);
+			return retval;
+		}
+	}
+
+	/* Notify the peer(s), assuming we have (an) association(s).
+	 * FIXME: for UDP, we have a 1-1-many mapping amongst sk, ep and asoc,
+	 *        so we don't have to do much work on locating associations.
+	 *
+	 * However, when the separation of ep and asoc kicks in, especially
+	 * for TCP style connection, it becomes n-1-n mapping.  We will need
+	 * to do more fine work.  Until then, hold my peace.
+	 *							--xguo
+	 *
+	 * Really, I don't think that will be a problem.  The bind()
+	 * call on a socket will either know the endpoint
+	 * (e.g. TCP-style listen()ing socket, or UDP-style socket),
+	 * or exactly one association.  The former case is EXACTLY
+	 * what we have now.  In the former case we know the
+	 * association already.					--piggy
+	 *
+	 * This code will be working on either a UDP style or a TCP style
+	 * socket, or say either an endpoint or an association. The socket
+	 * type verification code need to be added later before calling the
+	 * ADDIP code.
+	 * 							--daisy
+	 */
+
+#if CONFIG_IP_SCTP_ADDIP
+	/* Add these addresses to all associations on this endpoint.  */
+	if (retval >= 0) {
+		struct list_head *pos;
+		struct sctp_endpoint *ep;
+		struct sctp_association *asoc;
+		ep = sctp_sk(sk)->ep;
+
+		list_for_each(pos, &ep->asocs) {
+			asoc = list_entry(pos, struct sctp_association, asocs);
+
+			sctp_addip_addr_config(asoc,
+					       SCTP_PARAM_ADD_IP,
+					       addrs, addrcnt);
+		}
+	}
+#endif
+
+	return retval;
+}
+
+/* Remove a list of addresses from bind addresses list.  Do not remove the
+ * last address.
+ *
+ * Basically run through each address specified in the addrs/addrcnt
+ * array/length pair, determine if it is IPv6 or IPv4 and call
+ * sctp_del_bind() on it.
+ *
+ * If any of them fails, then the operation will be reversed and the
+ * ones that were removed will be added back.
+ *
+ * At least one address has to be left; if only one address is
+ * available, the operation will return -EBUSY.
+ *
+ * Only __sctp_bindx() is supposed to call this function.
+ */
+int sctp_bindx_rem(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt)
+{
+	struct sctp_opt *sp = sctp_sk(sk);
+	struct sctp_endpoint *ep = sp->ep;
+	int cnt;
+	sctp_bind_addr_t *bp = &ep->base.bind_addr;
+	int retval = 0;
+	union sctp_addr saveaddr;
+
+	SCTP_DEBUG_PRINTK("sctp_bindx_rem (sk: %p, addrs: %p, addrcnt: %d)\n",
+			  sk, addrs, addrcnt);
+
+	for (cnt = 0; cnt < addrcnt; cnt++) {
+		/* If there is only one bind address, there is nothing more
+		 * to be removed (we need at least one address here).
+		 */
+		if (list_empty(&bp->address_list)) {
+			retval = -EBUSY;
+			goto err_bindx_rem;
+		}
+
+		/* The list may contain either IPv4 or IPv6 address;
+		 * determine the address length for walking thru the list.
+		 */
+		switch (((struct sockaddr *)&addrs[cnt])->sa_family) {
+		case AF_INET:
+			saveaddr = *((union sctp_addr *)
+				     &addrs[cnt]);
+			saveaddr.v4.sin_port = ntohs(saveaddr.v4.sin_port);
+			/* Verify the port.  */
+			if (saveaddr.v4.sin_port != bp->port) {
+				retval = -EINVAL;
+				goto err_bindx_rem;
+			}
+			break;
+
+		case AF_INET6:
+			saveaddr = *((union sctp_addr *)
+				     &addrs[cnt]);
+			saveaddr.v6.sin6_port =
+				ntohs(saveaddr.v6.sin6_port);
+			/* verify the port */
+			if (saveaddr.v6.sin6_port != bp->port) {
+				retval = -EINVAL;
+				goto err_bindx_rem;
+			}
+			break;
+
+		default:
+			retval = -EINVAL;
+			goto err_bindx_rem;
+		};
+
+		/* FIXME - There is probably a need to check if sk->saddr and
+		 * sk->rcv_addr are currently set to one of the addresses to
+		 * be removed. This is something which needs to be looked into
+		 * when we are fixing the outstanding issues with multi-homing
+		 * socket routing and failover schemes. Refer to comments in
+		 * sctp_do_bind(). -daisy
+		 */
+		sctp_local_bh_disable();
+		sctp_write_lock(&ep->base.addr_lock);
+
+		retval = sctp_del_bind_addr(bp, &saveaddr);
+
+		sctp_write_unlock(&ep->base.addr_lock);
+		sctp_local_bh_enable();
+
+err_bindx_rem:
+		if (retval < 0) {
+			/* Failed. Add the ones that has been removed back */
+			if (cnt > 0)
+				sctp_bindx_add(sk, addrs, cnt);
+			return retval;
+		}
+	}
+
+	/*
+	 * This code will be working on either a UDP style or a TCP style
+	 * socket, * or say either an endpoint or an association. The socket
+	 * type verification code need to be added later before calling the
+	 * ADDIP code.
+	 * 							--daisy
+	 */
+#if CONFIG_IP_SCTP_ADDIP
+	/* Remove these addresses from all associations on this endpoint.  */
+	if (retval >= 0) {
+		struct list_head *pos;
+		struct sctp_endpoint *ep;
+		struct sctp_association *asoc;
+
+		ep = sctp_sk(sk)->ep;
+		list_for_each(pos, &ep->asocs) {
+			asoc = list_entry(pos, struct sctp_association, asocs);
+			sctp_addip_addr_config(asoc, SCTP_PARAM_DEL_IP,
+					       addrs, addrcnt);
+		}
+	}
+#endif
+	return retval;
+}
+
+/* Helper for tunneling sys_bindx() requests through sctp_setsockopt()
+ *
+ * Basically do nothing but copying the addresses from user to kernel
+ * land and invoking sctp_bindx on the sk. This is used for tunneling
+ * the sctp_bindx() [sys_bindx()] request through sctp_setsockopt()
+ * from userspace.
+ *
+ * Note I don't use move_addr_to_kernel(): the reason is we would be
+ * iterating over an array of struct sockaddr_storage passing always
+ * what we know is a good size (sizeof (struct sock...)), so it is
+ * pointless. Instead check the whole area for read access and copy
+ * it.
+ *
+ * We don't use copy_from_user() for optimization: we first do the
+ * sanity checks (buffer size -fast- and access check-healthy
+ * pointer); if all of those succeed, then we can alloc the memory
+ * (expensive operation) needed to copy the data to kernel. Then we do
+ * the copying without checking the user space area
+ * (__copy_from_user()).
+ *
+ * On exit there is no need to do sockfd_put(), sys_setsockopt() does
+ * it.
+ *
+ * sk        The sk of the socket
+ * addrs     The pointer to the addresses in user land
+ * addrssize Size of the addrs buffer
+ * op        Operation to perform (add or remove, see the flags of
+ *           sctp_bindx)
+ *
+ * Returns 0 if ok, <0 errno code on error.
+ */
+SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk,
+				      struct sockaddr_storage *addrs,
+				      int addrssize, int op)
+{
+	struct sockaddr_storage *kaddrs;
+	int err;
+	size_t addrcnt;
+
+	SCTP_DEBUG_PRINTK("sctp_do_setsocktopt_bindx: sk %p addrs %p"
+			  " addrssize %d opt %d\n", sk, addrs, addrssize, op);
+
+	/* Do we have an integer number of structs sockaddr_storage?  */
+	if (unlikely(addrssize <= 0 ||
+		     addrssize % sizeof(struct sockaddr_storage) != 0))
+		return -EINVAL;
+
+	/* Check the user passed a healthy pointer.  */
+	if (unlikely(!access_ok(VERIFY_READ, addrs, addrssize)))
+		return -EFAULT;
+
+	/* Alloc space for the address array in kernel memory.  */
+	kaddrs = (struct sockaddr_storage *) kmalloc(addrssize, GFP_KERNEL);
+	if (unlikely(!kaddrs))
+		return -ENOMEM;
+
+	if (copy_from_user(kaddrs, addrs, addrssize)) {
+		kfree(kaddrs);
+		return -EFAULT;
+	}
+
+	addrcnt = addrssize / sizeof(struct sockaddr_storage);
+	err = __sctp_bindx(sk, kaddrs, addrcnt, op);   /* Do the work. */
+	kfree(kaddrs);
+
+	return err;
+}
+
+/* API 3.1.4 close() - UDP Style Syntax
+ * Applications use close() to perform graceful shutdown (as described in
+ * Section 10.1 of [SCTP]) on ALL the associations currently represented
+ * by a UDP-style socket.
+ *
+ * The syntax is
+ *
+ *   ret = close(int sd);
+ *
+ *   sd      - the socket descriptor of the associations to be closed.
+ *
+ * To gracefully shutdown a specific association represented by the
+ * UDP-style socket, an application should use the sendmsg() call,
+ * passing no user data, but including the appropriate flag in the
+ * ancillary data (see Section xxxx).
+ *
+ * If sd in the close() call is a branched-off socket representing only
+ * one association, the shutdown is performed on that association only.
+ */
+SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
+{
+	struct sctp_endpoint *ep;
+	struct sctp_association *asoc;
+	struct list_head *pos, *temp;
+
+	SCTP_DEBUG_PRINTK("sctp_close(sk: 0x%p...)\n", sk);
+
+	sctp_lock_sock(sk);
+	sk->shutdown = SHUTDOWN_MASK;
+
+	ep = sctp_sk(sk)->ep;
+
+	/* Walk all associations on a socket, not on an endpoint.  */
+	list_for_each_safe(pos, temp, &ep->asocs) {
+		asoc = list_entry(pos, struct sctp_association, asocs);
+
+		/* A closed association can still be in the list if it
+		 * belongs to a TCP-style listening socket that is not
+		 * yet accepted.
+		 */
+		if ((SCTP_SOCKET_TCP == sctp_sk(sk)->type) &&
+		    (SCTP_STATE_CLOSED == asoc->state)) {
+			sctp_unhash_established(asoc);
+			sctp_association_free(asoc);
+		} else
+			sctp_primitive_SHUTDOWN(asoc, NULL);
+	}
+
+	/* Clean up any skbs sitting on the receive queue.  */
+	skb_queue_purge(&sk->receive_queue);
+	skb_queue_purge(&sctp_sk(sk)->pd_lobby);
+
+	/* This will run the backlog queue.  */
+	sctp_release_sock(sk);
+
+	/* Supposedly, no process has access to the socket, but
+	 * the net layers still may.
+	 */
+	sctp_local_bh_disable();
+	sctp_bh_lock_sock(sk);
+
+	/* Hold the sock, since inet_sock_release() will put sock_put()
+	 * and we have just a little more cleanup.
+	 */
+	sock_hold(sk);
+	inet_sock_release(sk);
+
+	sctp_bh_unlock_sock(sk);
+	sctp_local_bh_enable();
+
+	sock_put(sk);
+
+	SCTP_DBG_OBJCNT_DEC(sock);
+}
+
+/* Handle EPIPE error. */
+static int sctp_error(struct sock *sk, int flags, int err)
+{
+	if (err == -EPIPE)
+		err = sock_error(sk) ? : -EPIPE;
+	if (err == -EPIPE && !(flags & MSG_NOSIGNAL))
+		send_sig(SIGPIPE, current, 0);
+	return err;
+}
+
+/* API 3.1.3 sendmsg() - UDP Style Syntax
+ *
+ * An application uses sendmsg() and recvmsg() calls to transmit data to
+ * and receive data from its peer.
+ *
+ *  ssize_t sendmsg(int socket, const struct msghdr *message,
+ *                  int flags);
+ *
+ *  socket  - the socket descriptor of the endpoint.
+ *  message - pointer to the msghdr structure which contains a single
+ *            user message and possibly some ancillary data.
+ *
+ *            See Section 5 for complete description of the data
+ *            structures.
+ *
+ *  flags   - flags sent or received with the user message, see Section
+ *            5 for complete description of the flags.
+ *
+ * Note:  This function could use a rewrite especially when explicit
+ * connect support comes in.
+ */
+/* BUG:  We do not implement the equivalent of wait_for_tcp_memory(). */
+
+SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *);
+
+SCTP_STATIC int sctp_sendmsg(struct sock *sk, struct msghdr *msg, int msg_len)
+{
+	struct sctp_opt *sp;
+	struct sctp_endpoint *ep;
+	struct sctp_association *new_asoc=NULL, *asoc=NULL;
+	struct sctp_transport *transport;
+	sctp_chunk_t *chunk = NULL;
+	union sctp_addr to;
+	struct sockaddr *msg_name = NULL;
+	struct sctp_sndrcvinfo default_sinfo = { 0 };
+	struct sctp_sndrcvinfo *sinfo;
+	struct sctp_initmsg *sinit;
+	sctp_assoc_t associd = NULL;
+	sctp_cmsgs_t cmsgs = { 0 };
+	int err;
+	sctp_scope_t scope;
+	long timeo;
+	__u16 sinfo_flags = 0;
+	struct sk_buff_head chunks;
+	int msg_flags = msg->msg_flags;
+
+	SCTP_DEBUG_PRINTK("sctp_sendmsg(sk: %p, msg: %p, msg_len: %d)\n",
+			  sk, msg, msg_len);
+
+	err = 0;
+	sp = sctp_sk(sk);
+	ep = sp->ep;
+
+	SCTP_DEBUG_PRINTK("Using endpoint: %s.\n", ep->debug_name);
+
+	if ((SCTP_SOCKET_TCP == sp->type) &&
+	    (SCTP_SS_ESTABLISHED != sk->state)) {
+		err = -EPIPE;
+		goto out_nounlock;
+	}
+
+	/* Parse out the SCTP CMSGs.  */
+	err = sctp_msghdr_parse(msg, &cmsgs);
+
+	if (err) {
+		SCTP_DEBUG_PRINTK("msghdr parse err = %x\n", err);
+		goto out_nounlock;
+	}
+
+	/* Fetch the destination address for this packet.  This
+	 * address only selects the association--it is not necessarily
+	 * the address we will send to.
+	 * For a peeled-off socket, msg_name is ignored.
+	 */
+	if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sp->type) && msg->msg_name) {
+		int msg_namelen = msg->msg_namelen;
+
+		err = sctp_verify_addr(sk, (union sctp_addr *)msg->msg_name,
+				       msg_namelen);
+		if (err)
+			return err;
+
+		if (msg_namelen > sizeof(to))
+			msg_namelen = sizeof(to);
+		memcpy(&to, msg->msg_name, msg_namelen);
+		SCTP_DEBUG_PRINTK("Just memcpy'd. msg_name is "
+				  "0x%x:%u.\n",
+				  to.v4.sin_addr.s_addr, to.v4.sin_port);
+
+		to.v4.sin_port = ntohs(to.v4.sin_port);
+		msg_name = msg->msg_name;
+	}
+
+	sinfo = cmsgs.info;
+	sinit = cmsgs.init;
+
+	/* Did the user specify SNDRCVINFO?  */
+	if (sinfo) {
+		sinfo_flags = sinfo->sinfo_flags;
+		associd = sinfo->sinfo_assoc_id;
+	}
+
+	SCTP_DEBUG_PRINTK("msg_len: %Zd, sinfo_flags: 0x%x\n",
+			  msg_len, sinfo_flags);
+
+	/* If MSG_EOF is set, no data can be sent. Disallow sending zero
+	 * length messages when MSG_EOF|MSG_ABORT is not set.
+	 * If MSG_ABORT is set, the message length could be non zero with
+	 * the msg_iov set to the user abort reason.
+ 	 */
+	if (((sinfo_flags & MSG_EOF) && (msg_len > 0)) ||
+	    (!(sinfo_flags & (MSG_EOF|MSG_ABORT)) && (msg_len == 0))) {
+		err = -EINVAL;
+		goto out_nounlock;
+	}
+
+	sctp_lock_sock(sk);
+
+	transport = NULL;
+
+	SCTP_DEBUG_PRINTK("About to look up association.\n");
+
+	/* If a msg_name has been specified, assume this is to be used.  */
+	if (msg_name) {
+		/* Look for a matching association on the endpoint. */
+		asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport);
+		if (!asoc) {
+			/* If we could not find a matching association on the
+			 * endpoint, make sure that there is no peeled-off
+			 * association on another socket.
+			 */
+			if (sctp_endpoint_is_peeled_off(ep, &to)) {
+				err = -EADDRNOTAVAIL;
+				goto out_unlock;
+			}
+		}
+	} else {
+		asoc = sctp_id2assoc(sk, associd);
+		if (!asoc) {
+			err = -EINVAL;
+			goto out_unlock;
+		}
+	}
+
+	if (asoc) {
+		SCTP_DEBUG_PRINTK("Just looked up association: "
+				  "%s. \n", asoc->debug_name);
+
+		/* We cannot send a message on a TCP-style SCTP_SS_ESTABLISHED 
+		 * socket that has an association in CLOSED state. This can
+		 * happen when an accepted socket has an association that is
+		 * already CLOSED. 
+		 */
+		if ((SCTP_STATE_CLOSED == asoc->state) &&
+		    (SCTP_SOCKET_TCP == sp->type)) {
+			err = -EPIPE;
+			goto out_unlock;
+		}
+
+		if (sinfo_flags & MSG_EOF) {
+			SCTP_DEBUG_PRINTK("Shutting down association: %p\n",
+					  asoc);
+			sctp_primitive_SHUTDOWN(asoc, NULL);
+			err = 0;
+			goto out_unlock;
+		}
+		if (sinfo_flags & MSG_ABORT) {
+			SCTP_DEBUG_PRINTK("Aborting association: %p\n", asoc);
+			sctp_primitive_ABORT(asoc, msg);
+			err = 0;
+			goto out_unlock;
+		}
+	}
+
+	/* Do we need to create the association?  */
+	if (!asoc) {
+		SCTP_DEBUG_PRINTK("There is no association yet.\n");
+
+		/* Check for invalid stream against the stream counts,
+		 * either the default or the user specified stream counts.
+		 */
+		if (sinfo) {
+			if (!sinit || (sinit && !sinit->sinit_num_ostreams)) {
+				/* Check against the defaults. */
+				if (sinfo->sinfo_stream >=
+				    sp->initmsg.sinit_num_ostreams) {
+					err = -EINVAL;
+					goto out_unlock;
+				}
+			} else {
+				/* Check against the requested.  */
+				if (sinfo->sinfo_stream >=
+				    sinit->sinit_num_ostreams) {
+					err = -EINVAL;
+					goto out_unlock;
+				}
+			}
+		}
+
+		/*
+		 * API 3.1.2 bind() - UDP Style Syntax
+		 * If a bind() or sctp_bindx() is not called prior to a
+		 * sendmsg() call that initiates a new association, the
+		 * system picks an ephemeral port and will choose an address
+		 * set equivalent to binding with a wildcard address.
+		 */
+		if (!ep->base.bind_addr.port) {
+			if (sctp_autobind(sk)) {
+				err = -EAGAIN;
+				goto out_unlock;
+			}
+		}
+
+		scope = sctp_scope(&to);
+		new_asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
+		if (!new_asoc) {
+			err = -ENOMEM;
+			goto out_unlock;
+		}
+		asoc = new_asoc;
+
+		/* If the SCTP_INIT ancillary data is specified, set all
+		 * the association init values accordingly.
+		 */
+		if (sinit) {
+			if (sinit->sinit_num_ostreams) {
+				asoc->c.sinit_num_ostreams =
+					sinit->sinit_num_ostreams;
+			}
+			if (sinit->sinit_max_instreams) {
+				asoc->c.sinit_max_instreams =
+					sinit->sinit_max_instreams;
+			}
+			if (sinit->sinit_max_attempts) {
+				asoc->max_init_attempts
+					= sinit->sinit_max_attempts;
+			}
+			if (sinit->sinit_max_init_timeo) {
+				asoc->max_init_timeo
+					= sinit->sinit_max_init_timeo * HZ;
+			}
+		}
+
+		/* Prime the peer's transport structures.  */
+		transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL);
+		if (!transport) {
+			err = -ENOMEM;
+			goto out_free;
+		}
+		err = sctp_assoc_set_bind_addr_from_ep(asoc, GFP_KERNEL);
+		if (err < 0) {
+			err = -ENOMEM;
+			goto out_free;
+		}
+	}
+
+	/* ASSERT: we have a valid association at this point.  */
+	SCTP_DEBUG_PRINTK("We have a valid association.\n");
+
+	if (!sinfo) {
+		/* If the user didn't specify SNDRCVINFO, make up one with
+		 * some defaults.
+		 */
+		default_sinfo.sinfo_stream = asoc->defaults.stream;
+		default_sinfo.sinfo_flags = asoc->defaults.flags;
+		default_sinfo.sinfo_ppid = asoc->defaults.ppid;
+		default_sinfo.sinfo_context = asoc->defaults.context;
+		default_sinfo.sinfo_timetolive = asoc->defaults.timetolive;
+		default_sinfo.sinfo_assoc_id = sctp_assoc2id(asoc);
+		sinfo = &default_sinfo;
+	}
+
+	/* API 7.1.7, the sndbuf size per association bounds the
+	 * maximum size of data that can be sent in a single send call.
+	 */
+	if (msg_len > sk->sndbuf) {
+		err = -EMSGSIZE;
+		goto out_free;
+	}
+
+	/* If fragmentation is disabled and the message length exceeds the
+	 * association fragmentation point, return EMSGSIZE.  The I-D
+	 * does not specify what this error is, but this looks like
+	 * a great fit.
+	 */
+	if (sctp_sk(sk)->disable_fragments && (msg_len > asoc->frag_point)) {
+		err = -EMSGSIZE;
+		goto out_free;
+	}
+
+	if (sinfo) {
+		/* Check for invalid stream. */
+		if (sinfo->sinfo_stream >= asoc->c.sinit_num_ostreams) {
+			err = -EINVAL;
+			goto out_free;
+		}
+	}
+
+	timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
+	if (!sctp_wspace(asoc)) {
+		err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len);
+		if (err)
+			goto out_free;
+	}
+
+	/* Break the message into multiple chunks of maximum size. */
+	skb_queue_head_init(&chunks);
+	err = sctp_datachunks_from_user(asoc, sinfo, msg, msg_len, &chunks);
+	if (err)
+		goto out_free;
+
+	/* Auto-connect, if we aren't connected already. */
+	if (SCTP_STATE_CLOSED == asoc->state) {
+		err = sctp_primitive_ASSOCIATE(asoc, NULL);
+		if (err < 0)
+			goto out_free;
+		SCTP_DEBUG_PRINTK("We associated primitively.\n");
+	}
+
+	/* Now send the (possibly) fragmented message. */
+	while ((chunk = (sctp_chunk_t *)__skb_dequeue(&chunks))) {
+
+		/* Do accounting for the write space.  */
+		sctp_set_owner_w(chunk);
+
+		/* This flag, in the UDP model, requests the SCTP stack to
+		 * override the primary destination address with the
+		 * address found with the sendto/sendmsg call.
+		 */
+		if (sinfo_flags & MSG_ADDR_OVER) {
+			if (!msg->msg_name) {
+				err = -EINVAL;
+				goto out_free;
+			}
+			chunk->transport = sctp_assoc_lookup_paddr(asoc, &to);
+			if (!chunk->transport) {
+				err = -EINVAL;
+				goto out_free;
+			}
+		}
+
+		/* Send it to the lower layers.  */
+		sctp_primitive_SEND(asoc, chunk);
+		SCTP_DEBUG_PRINTK("We sent primitively.\n");
+	}
+
+	if (!err) {
+		err = msg_len;
+		goto out_unlock;
+	}
+	/* If we are already past ASSOCIATE, the lower
+	 * layers are responsible for association cleanup.
+	 */
+	goto out_free_chunk;
+
+out_free:
+	if (new_asoc)
+		sctp_association_free(asoc);
+
+out_free_chunk:
+	if (chunk)
+		sctp_free_chunk(chunk);
+
+out_unlock:
+	sctp_release_sock(sk);
+
+out_nounlock:
+	return sctp_error(sk, msg_flags, err);
+
+#if 0
+do_sock_err:
+	if (msg_len)
+		err = msg_len;
+	else
+		err = sock_error(sk);
+	goto out;
+
+do_interrupted:
+	if (msg_len)
+		err = msg_len;
+	goto out;
+#endif /* 0 */
+}
+
+/* This is an extended version of skb_pull() that removes the data from the
+ * start of a skb even when data is spread across the list of skb's in the
+ * frag_list. len specifies the total amount of data that needs to be removed.
+ * when 'len' bytes could be removed from the skb, it returns 0.
+ * If 'len' exceeds the total skb length,  it returns the no. of bytes that
+ * could not be removed.
+ */
+static int sctp_skb_pull(struct sk_buff *skb, int len)
+{
+	struct sk_buff *list;
+	int skb_len = skb_headlen(skb);
+	int rlen;
+
+	if (len <= skb_len) {
+		__skb_pull(skb, len);
+		return 0;
+	}
+	len -= skb_len;
+	__skb_pull(skb, skb_len);
+
+	for (list = skb_shinfo(skb)->frag_list; list; list = list->next) {
+		rlen = sctp_skb_pull(list, len);
+		skb->len -= (len-rlen);
+		skb->data_len -= (len-rlen);
+
+		if (!rlen)
+			return 0;
+
+		len = rlen;
+	}
+
+	return len;
+}
+
+/* API 3.1.3  recvmsg() - UDP Style Syntax
+ *
+ *  ssize_t recvmsg(int socket, struct msghdr *message,
+ *                    int flags);
+ *
+ *  socket  - the socket descriptor of the endpoint.
+ *  message - pointer to the msghdr structure which contains a single
+ *            user message and possibly some ancillary data.
+ *
+ *            See Section 5 for complete description of the data
+ *            structures.
+ *
+ *  flags   - flags sent or received with the user message, see Section
+ *            5 for complete description of the flags.
+ */
+static struct sk_buff *sctp_skb_recv_datagram(struct sock *, int, int, int *);
+
+SCTP_STATIC int sctp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
+			     int noblock, int flags, int *addr_len)
+{
+	struct sctp_ulpevent *event = NULL;
+	struct sctp_opt *sp = sctp_sk(sk);
+	struct sk_buff *skb;
+	int copied;
+	int err = 0;
+	int skb_len;
+
+	SCTP_DEBUG_PRINTK("sctp_recvmsg(%s: %p, %s: %p, %s: %d, %s: %d, %s: "
+			  "0x%x, %s: %p)\n", "sk", sk, "msghdr", msg,
+			  "len", len, "knoblauch", noblock,
+			  "flags", flags, "addr_len", addr_len);
+
+	sctp_lock_sock(sk);
+
+	if ((SCTP_SOCKET_TCP == sp->type) &&
+	    (SCTP_SS_ESTABLISHED != sk->state)) {
+		err = -ENOTCONN;
+		goto out;
+	}
+
+	skb = sctp_skb_recv_datagram(sk, flags, noblock, &err);
+	if (!skb)
+		goto out;
+
+	/* Get the total length of the skb including any skb's in the
+	 * frag_list.
+	 */
+	skb_len = skb->len;
+
+	copied = skb_len;
+	if (copied > len)
+		copied = len;
+
+	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+
+	event = sctp_skb2event(skb);
+
+	if (err)
+		goto out_free;
+
+	sock_recv_timestamp(msg, sk, skb);
+	if (sctp_ulpevent_is_notification(event)) {
+		msg->msg_flags |= MSG_NOTIFICATION;
+		sp->pf->event_msgname(event, msg->msg_name, addr_len);
+	} else {
+		sp->pf->skb_msgname(skb, msg->msg_name, addr_len);
+	}
+
+	/* Check if we allow SCTP_SNDRCVINFO. */
+	if (sp->subscribe.sctp_data_io_event)
+		sctp_ulpevent_read_sndrcvinfo(event, msg);
+#if 0
+	/* FIXME: we should be calling IP/IPv6 layers.  */
+	if (sk->protinfo.af_inet.cmsg_flags)
+		ip_cmsg_recv(msg, skb);
+#endif
+
+	err = copied;
+
+	/* If skb's length exceeds the user's buffer, update the skb and
+	 * push it back to the receive_queue so that the next call to
+	 * recvmsg() will return the remaining data. Don't set MSG_EOR.
+	 */
+	if (skb_len > copied) {
+		msg->msg_flags &= ~MSG_EOR;
+		if (flags & MSG_PEEK)
+			goto out_free;
+		sctp_skb_pull(skb, copied);
+		skb_queue_head(&sk->receive_queue, skb);
+
+		/* When only partial message is copied to the user, increase
+		 * rwnd by that amount. If all the data in the skb is read,
+		 * rwnd is updated when the skb's destructor is called via
+		 * sctp_ulpevent_free().
+		 */
+		sctp_assoc_rwnd_increase(event->event_asoc, copied);
+		goto out;
+	} else if ((event->msg_flags & MSG_NOTIFICATION) ||
+		   (event->msg_flags & MSG_EOR))
+		msg->msg_flags |= MSG_EOR;
+	else
+		msg->msg_flags &= ~MSG_EOR;
+
+out_free:
+	sctp_ulpevent_free(event); /* Free the skb. */
+out:
+	sctp_release_sock(sk);
+	return err;
+}
+
+static int sctp_setsockopt_disable_fragments(struct sock *sk,
+						    char *optval, int optlen)
+{
+	int val;
+
+	if (optlen < sizeof(int))
+		return -EINVAL;
+
+	if (get_user(val, (int *)optval))
+		return -EFAULT;
+
+	sctp_sk(sk)->disable_fragments = (val == 0) ? 0 : 1;
+
+	return 0;
+}
+
+static int sctp_setsockopt_events(struct sock *sk, char *optval,
+					int optlen)
+{
+	if (optlen != sizeof(struct sctp_event_subscribe))
+		return -EINVAL;
+	if (copy_from_user(&sctp_sk(sk)->subscribe, optval, optlen))
+		return -EFAULT;
+	return 0;
+}
+
+static int sctp_setsockopt_autoclose(struct sock *sk, char *optval,
+					    int optlen)
+{
+	struct sctp_opt *sp = sctp_sk(sk);
+
+	/* Applicable to UDP-style socket only */
+	if (SCTP_SOCKET_TCP == sp->type)
+		return -EOPNOTSUPP;
+	if (optlen != sizeof(int))
+		return -EINVAL;
+	if (copy_from_user(&sp->autoclose, optval, optlen))
+		return -EFAULT;
+
+	sp->ep->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE] = sp->autoclose * HZ;
+	return 0;
+}
+
+static int sctp_setsockopt_peer_addr_params(struct sock *sk,
+					    char *optval, int optlen)
+{
+	struct sctp_paddrparams params;
+	struct sctp_association *asoc;
+	union sctp_addr *addr;
+	struct sctp_transport *trans;
+	int error;
+
+	if (optlen != sizeof(struct sctp_paddrparams))
+		return -EINVAL;
+	if (copy_from_user(&params, optval, optlen))
+		return -EFAULT;
+
+	asoc = sctp_id2assoc(sk, params.spp_assoc_id);
+	if (!asoc)
+		return -EINVAL;
+
+	addr = (union sctp_addr *) &(params.spp_address);
+
+	trans = sctp_assoc_lookup_paddr(asoc, addr);
+	if (!trans)
+		return -ENOENT;
+
+	/* Applications can enable or disable heartbeats for any peer address
+	 * of an association, modify an address's heartbeat interval, force a
+	 * heartbeat to be sent immediately, and adjust the address's maximum
+	 * number of retransmissions sent before an address is considered
+	 * unreachable.
+	 *
+	 * The value of the heartbeat interval, in milliseconds. A value of
+	 * UINT32_MAX (4294967295), when modifying the parameter, specifies
+	 * that a heartbeat should be sent immediately to the peer address,
+	 * and the current interval should remain unchanged.
+	 */
+	if (0xffffffff == params.spp_hbinterval) {
+		error = sctp_primitive_REQUESTHEARTBEAT (asoc, trans);
+		if (error)
+			return error;
+	} else {
+	/* The value of the heartbeat interval, in milliseconds. A value of 0,
+	 * when modifying the parameter, specifies that the heartbeat on this
+	 * address should be disabled.
+	 */
+		if (params.spp_hbinterval) {
+			trans->hb_allowed = 1;
+			trans->hb_interval = params.spp_hbinterval * HZ / 1000;
+		} else
+			trans->hb_allowed = 0;
+	}
+
+	/* spp_pathmaxrxt contains the maximum number of retransmissions
+	 * before this address shall be considered unreachable.
+	 */
+	trans->error_threshold = params.spp_pathmaxrxt;
+
+	return 0;
+}
+
+static int sctp_setsockopt_initmsg(struct sock *sk, char *optval, int optlen)
+{
+	if (optlen != sizeof(struct sctp_initmsg))
+		return -EINVAL;
+	if (copy_from_user(&sctp_sk(sk)->initmsg, optval, optlen))
+		return -EFAULT;
+	return 0;
+}
+
+/*
+ * 7.1.15 Set default send parameters (SET_DEFAULT_SEND_PARAM)
+ *
+ *   Applications that wish to use the sendto() system call may wish to
+ *   specify a default set of parameters that would normally be supplied
+ *   through the inclusion of ancillary data.  This socket option allows
+ *   such an application to set the default sctp_sndrcvinfo structure.
+ *   The application that wishes to use this socket option simply passes
+ *   in to this call the sctp_sndrcvinfo structure defined in Section
+ *   5.2.2) The input parameters accepted by this call include
+ *   sinfo_stream, sinfo_flags, sinfo_ppid, sinfo_context,
+ *   sinfo_timetolive.  The user must provide the sinfo_assoc_id field in
+ *   to this call if the caller is using the UDP model.
+ */
+static int sctp_setsockopt_default_send_param(struct sock *sk,
+						char *optval, int optlen)
+{
+	struct sctp_sndrcvinfo info;
+	struct sctp_association *asoc;
+
+	if (optlen != sizeof(struct sctp_sndrcvinfo))
+		return -EINVAL;
+	if (copy_from_user(&info, optval, optlen))
+		return -EFAULT;
+
+	asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
+	if (!asoc)
+		return -EINVAL;
+
+	asoc->defaults.stream = info.sinfo_stream;
+	asoc->defaults.flags = info.sinfo_flags;
+	asoc->defaults.ppid = info.sinfo_ppid;
+	asoc->defaults.context = info.sinfo_context;
+	asoc->defaults.timetolive = info.sinfo_timetolive;
+	return 0;
+}
+
+/* 7.1.10 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR)
+ *
+ * Requests that the local SCTP stack use the enclosed peer address as
+ * the association primary.  The enclosed address must be one of the
+ * association peer's addresses.
+ */
+static int sctp_setsockopt_peer_prim(struct sock *sk, char *optval, int optlen)
+{
+	struct sctp_setpeerprim prim;
+	struct sctp_association *asoc;
+	union sctp_addr *addr;
+	struct sctp_transport *trans;
+
+	if (optlen != sizeof(struct sctp_setpeerprim))
+		return -EINVAL;
+
+	if (copy_from_user(&prim, optval, sizeof(struct sctp_setpeerprim)))
+		return -EFAULT;
+
+	asoc = sctp_id2assoc(sk, prim.sspp_assoc_id);
+	if (!asoc)
+		return -EINVAL;
+
+	/* Find the requested address. */
+	addr = (union sctp_addr *) &(prim.sspp_addr);
+
+	trans = sctp_assoc_lookup_paddr(asoc, addr);
+	if (!trans)
+		return -ENOENT;
+
+	sctp_assoc_set_primary(asoc, trans);
+
+	return 0;
+}
+
+/*
+ *
+ * 7.1.5 SCTP_NODELAY
+ *
+ * Turn on/off any Nagle-like algorithm.  This means that packets are
+ * generally sent as soon as possible and no unnecessary delays are
+ * introduced, at the cost of more packets in the network.  Expects an
+ *  integer boolean flag.
+ */
+static int sctp_setsockopt_nodelay(struct sock *sk, char *optval,
+					int optlen)
+{
+	__u8 val;
+
+	if (optlen < sizeof(__u8))
+		return -EINVAL;
+	if (get_user(val, (__u8 *)optval))
+		return -EFAULT;
+
+	sctp_sk(sk)->nodelay = (val == 0) ? 0 : 1;
+	return 0;
+}
+
+/* API 6.2 setsockopt(), getsockopt()
+ *
+ * Applications use setsockopt() and getsockopt() to set or retrieve
+ * socket options.  Socket options are used to change the default
+ * behavior of sockets calls.  They are described in Section 7.
+ *
+ * The syntax is:
+ *
+ *   ret = getsockopt(int sd, int level, int optname, void *optval,
+ *                    int *optlen);
+ *   ret = setsockopt(int sd, int level, int optname, const void *optval,
+ *                    int optlen);
+ *
+ *   sd      - the socket descript.
+ *   level   - set to IPPROTO_SCTP for all SCTP options.
+ *   optname - the option name.
+ *   optval  - the buffer to store the value of the option.
+ *   optlen  - the size of the buffer.
+ */
+SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
+				char *optval, int optlen)
+{
+	int retval = 0;
+	char *tmp;
+
+	SCTP_DEBUG_PRINTK("sctp_setsockopt(sk: %p... optname: %d)\n",
+			  sk, optname);
+
+	/* I can hardly begin to describe how wrong this is.  This is
+	 * so broken as to be worse than useless.  The API draft
+	 * REALLY is NOT helpful here...  I am not convinced that the
+	 * semantics of setsockopt() with a level OTHER THAN SOL_SCTP
+	 * are at all well-founded.
+	 */
+	if (level != SOL_SCTP) {
+		struct sctp_af *af = sctp_sk(sk)->pf->af;
+		retval = af->setsockopt(sk, level, optname, optval, optlen);
+		goto out_nounlock;
+	}
+
+	sctp_lock_sock(sk);
+
+	switch (optname) {
+	case SCTP_SOCKOPT_DEBUG_NAME:
+		/* BUG! we don't ever seem to free this memory. --jgrimm */
+		if (NULL == (tmp = kmalloc(optlen + 1, GFP_KERNEL))) {
+			retval = -ENOMEM;
+			goto out_unlock;
+		}
+
+		if (copy_from_user(tmp, optval, optlen)) {
+			retval = -EFAULT;
+			goto out_unlock;
+		}
+		tmp[optlen] = '\000';
+		sctp_sk(sk)->ep->debug_name = tmp;
+		break;
+
+	case SCTP_SOCKOPT_BINDX_ADD:
+		/* 'optlen' is the size of the addresses buffer. */
+		retval = sctp_setsockopt_bindx(sk, (struct sockaddr_storage *)
+					       optval, optlen, BINDX_ADD_ADDR);
+		break;
+
+	case SCTP_SOCKOPT_BINDX_REM:
+		/* 'optlen' is the size of the addresses buffer. */
+		retval = sctp_setsockopt_bindx(sk, (struct sockaddr_storage *)
+					       optval, optlen, BINDX_REM_ADDR);
+		break;
+
+	case SCTP_DISABLE_FRAGMENTS:
+		retval = sctp_setsockopt_disable_fragments(sk, optval, optlen);
+		break;
+
+	case SCTP_SET_EVENTS:
+		retval = sctp_setsockopt_events(sk, optval, optlen);
+		break;
+
+	case SCTP_AUTOCLOSE:
+		retval = sctp_setsockopt_autoclose(sk, optval, optlen);
+		break;
+
+	case SCTP_SET_PEER_ADDR_PARAMS:
+		retval = sctp_setsockopt_peer_addr_params(sk, optval, optlen);
+		break;
+
+	case SCTP_INITMSG:
+		retval = sctp_setsockopt_initmsg(sk, optval, optlen);
+		break;
+
+	case SCTP_SET_DEFAULT_SEND_PARAM:
+		retval = sctp_setsockopt_default_send_param(sk, optval,
+							    optlen);
+		break;
+
+	case SCTP_SET_PEER_PRIMARY_ADDR:
+		retval = sctp_setsockopt_peer_prim(sk, optval, optlen);
+		break;
+
+	case SCTP_NODELAY:
+		retval = sctp_setsockopt_nodelay(sk, optval, optlen);
+		break;
+
+	default:
+		retval = -ENOPROTOOPT;
+		break;
+	};
+
+out_unlock:
+	sctp_release_sock(sk);
+
+out_nounlock:
+	return retval;
+}
+
+/* API 3.1.6 connect() - UDP Style Syntax
+ *
+ * An application may use the connect() call in the UDP model to initiate an
+ * association without sending data.
+ *
+ * The syntax is:
+ *
+ * ret = connect(int sd, const struct sockaddr *nam, socklen_t len);
+ *
+ * sd: the socket descriptor to have a new association added to.
+ *
+ * nam: the address structure (either struct sockaddr_in or struct
+ *    sockaddr_in6 defined in RFC2553 [7]).
+ *
+ * len: the size of the address.
+ */
+SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
+			     int addr_len)
+{
+	struct sctp_opt *sp;
+	struct sctp_endpoint *ep;
+	struct sctp_association *asoc;
+	struct sctp_transport *transport;
+	union sctp_addr to;
+	struct sctp_af *af;
+	sctp_scope_t scope;
+	long timeo;
+	int err = 0;
+
+	sctp_lock_sock(sk);
+
+	SCTP_DEBUG_PRINTK("%s - sk: %p, sockaddr: %p, addr_len: %d)\n",
+			  __FUNCTION__, sk, uaddr, addr_len);
+
+	sp = sctp_sk(sk);
+	ep = sp->ep;
+
+	/* connect() cannot be done on a socket that is already in ESTABLISHED
+	 * state - UDP-style peeled off socket or a TCP-style socket that
+	 * is already connected.
+	 * It cannot be done even on a TCP-style listening socket.
+	 */
+	if ((SCTP_SS_ESTABLISHED == sk->state) ||
+	    ((SCTP_SOCKET_TCP == sp->type) &&
+	     (SCTP_SS_LISTENING == sk->state))) {
+		err = -EISCONN;
+		goto out_unlock;
+	}
+
+	err = sctp_verify_addr(sk, (union sctp_addr *)uaddr, addr_len);
+	if (err)
+		goto out_unlock;
+
+	if (addr_len > sizeof(to))
+		addr_len = sizeof(to);
+	memcpy(&to, uaddr, addr_len);
+	to.v4.sin_port = ntohs(to.v4.sin_port);
+
+	asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport);
+	if (asoc) {
+		if (asoc->state >= SCTP_STATE_ESTABLISHED)
+			err = -EISCONN;
+		else
+			err = -EALREADY;
+		goto out_unlock;
+	}
+
+	/* If we could not find a matching association on the endpoint,
+	 * make sure that there is no peeled-off association matching the
+	 * peer address even on another socket.
+	 */
+	if (sctp_endpoint_is_peeled_off(ep, &to)) {
+		err = -EADDRNOTAVAIL;
+		goto out_unlock;
+	}
+
+	/* If a bind() or sctp_bindx() is not called prior to a connect()
+	 * call, the system picks an ephemeral port and will choose an address
+	 * set equivalent to binding with a wildcard address.
+	 */
+	if (!ep->base.bind_addr.port) {
+		if (sctp_autobind(sk)) {
+			err = -EAGAIN;
+			goto out_unlock;
+		}
+	}
+
+	scope = sctp_scope(&to);
+	asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
+	if (!asoc) {
+		err = -ENOMEM;
+		goto out_unlock;
+  	}
+
+	/* Prime the peer's transport structures.  */
+	transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL);
+	if (!transport) {
+		sctp_association_free(asoc);
+		goto out_unlock;
+	}
+	err = sctp_assoc_set_bind_addr_from_ep(asoc, GFP_KERNEL);
+	if (err < 0) {
+		sctp_association_free(asoc);
+		goto out_unlock;
+	}
+
+	err = sctp_primitive_ASSOCIATE(asoc, NULL);
+	if (err < 0) {
+		sctp_association_free(asoc);
+		goto out_unlock;
+	}
+
+	/* Initialize sk's dport and daddr for getpeername() */
+	(sk)->dport = htons(asoc->peer.port);
+	af = sctp_get_af_specific(to.sa.sa_family);
+	af->to_sk_daddr(&to, sk); 
+
+	timeo = sock_sndtimeo(sk, sk->socket->file->f_flags & O_NONBLOCK);
+	err = sctp_wait_for_connect(asoc, &timeo);
+
+out_unlock:
+	sctp_release_sock(sk);
+
+	return err;
+}
+
+/* FIXME: Write comments. */
+SCTP_STATIC int sctp_disconnect(struct sock *sk, int flags)
+{
+	return -EOPNOTSUPP; /* STUB */
+}
+
+/* 4.1.4 accept() - TCP Style Syntax
+ *
+ * Applications use accept() call to remove an established SCTP
+ * association from the accept queue of the endpoint.  A new socket
+ * descriptor will be returned from accept() to represent the newly
+ * formed association.
+ */
+SCTP_STATIC struct sock *sctp_accept(struct sock *sk, int flags, int *err)
+{
+	struct sctp_opt *sp;
+	struct sctp_endpoint *ep;
+	struct sock *newsk = NULL;
+	struct sctp_association *asoc;
+	long timeo;
+	int error = 0;
+
+	sctp_lock_sock(sk);
+
+	sp = sctp_sk(sk);
+	ep = sp->ep;
+
+	if (SCTP_SOCKET_TCP != sp->type) {
+		error = -EOPNOTSUPP;
+		goto out;
+	}
+
+	if (SCTP_SS_LISTENING != sk->state) {
+		error = -EINVAL;
+		goto out;
+	}
+
+	timeo = sock_rcvtimeo(sk, sk->socket->file->f_flags & O_NONBLOCK);
+
+	error = sctp_wait_for_accept(sk, timeo);
+	if (error)
+		goto out;
+
+	/* We treat the list of associations on the endpoint as the accept
+	 * queue and pick the first association on the list.
+	 */
+	asoc = list_entry(ep->asocs.next, struct sctp_association, asocs);
+
+	newsk = sp->pf->create_accept_sk(sk, asoc);
+	if (!newsk) {
+		error = -ENOMEM;
+		goto out;
+	}
+
+	/* Populate the fields of the newsk from the oldsk and migrate the
+	 * asoc to the newsk.
+	 */
+	sctp_sock_migrate(sk, newsk, asoc, SCTP_SOCKET_TCP);
+
+out:
+	sctp_release_sock(sk);
+ 	*err = error;
+	return newsk;
+}
+
+/* The SCTP ioctl handler. */
+SCTP_STATIC int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+
+/* This is the function which gets called during socket creation to
+ * initialized the SCTP-specific portion of the sock.
+ * The sock structure should already be zero-filled memory.
+ */
+SCTP_STATIC int sctp_init_sock(struct sock *sk)
+{
+	struct sctp_endpoint *ep;
+	struct sctp_protocol *proto;
+	struct sctp_opt *sp;
+
+	SCTP_DEBUG_PRINTK("sctp_init_sock(sk: %p)\n", sk);
+
+	proto = sctp_get_protocol();
+
+	sp = sctp_sk(sk);
+
+	/* Initialize the SCTP per socket area.  */
+	switch (sk->type) {
+	case SOCK_SEQPACKET:
+		sp->type = SCTP_SOCKET_UDP;
+		break;
+	case SOCK_STREAM:
+		sp->type = SCTP_SOCKET_TCP;
+		break;
+	default:
+		return -ESOCKTNOSUPPORT;
+	}
+
+	/* FIXME:  The next draft (04) of the SCTP Sockets Extensions
+	 * should include a socket option for manipulating these
+	 * message parameters (and a few others).
+	 */
+	sp->default_stream = 0;
+	sp->default_ppid = 0;
+
+	/* Initialize default setup parameters. These parameters
+	 * can be modified with the SCTP_INITMSG socket option or
+	 * overridden by the SCTP_INIT CMSG.
+	 */
+	sp->initmsg.sinit_num_ostreams   = proto->max_outstreams;
+	sp->initmsg.sinit_max_instreams  = proto->max_instreams;
+	sp->initmsg.sinit_max_attempts   = proto->max_retrans_init;
+	sp->initmsg.sinit_max_init_timeo = proto->rto_max / HZ;
+
+	/* Initialize default RTO related parameters.  These parameters can
+	 * be modified for with the SCTP_RTOINFO socket option.
+	 * FIXME: These are not used yet.
+	 */
+	sp->rtoinfo.srto_initial = proto->rto_initial;
+	sp->rtoinfo.srto_max     = proto->rto_max;
+	sp->rtoinfo.srto_min     = proto->rto_min;
+
+	/* Initialize default event subscriptions.
+	 * the struct sock is initialized to zero, so only
+	 * enable the events needed.  By default, UDP-style
+	 * sockets enable io and association change notifications.
+	 */
+	if (SCTP_SOCKET_UDP == sp->type) {
+		sp->subscribe.sctp_data_io_event     = 1;
+		sp->subscribe.sctp_association_event = 1;
+	}
+
+	/* Default Peer Address Parameters.  These defaults can
+	 * be modified via SCTP_SET_PEER_ADDR_PARAMS
+	 */
+	sp->paddrparam.spp_hbinterval = proto->hb_interval / HZ;
+	sp->paddrparam.spp_pathmaxrxt = proto->max_retrans_path;
+
+	/* If enabled no SCTP message fragmentation will be performed.
+	 * Configure through SCTP_DISABLE_FRAGMENTS socket option.
+	 */
+	sp->disable_fragments = 0;
+
+	/* Turn on/off any Nagle-like algorithm.  */
+	sp->nodelay           = 1;
+
+	/* Auto-close idle associations after the configured
+	 * number of seconds.  A value of 0 disables this
+	 * feature.  Configure through the SCTP_AUTOCLOSE socket option,
+	 * for UDP-style sockets only.
+	 */
+	sp->autoclose         = 0;
+	sp->pf = sctp_get_pf_specific(sk->family);
+
+	/* Control variables for partial data delivery. */
+	sp->pd_mode           = 0;
+	skb_queue_head_init(&sp->pd_lobby);
+
+	/* Create a per socket endpoint structure.  Even if we
+	 * change the data structure relationships, this may still
+	 * be useful for storing pre-connect address information.
+	 */
+	ep = sctp_endpoint_new(sk, GFP_KERNEL);
+	if (!ep)
+		return -ENOMEM;
+
+	sp->ep = ep;
+	sp->hmac = NULL;
+
+	SCTP_DBG_OBJCNT_INC(sock);
+	return 0;
+}
+
+/* Cleanup any SCTP per socket resources.  */
+SCTP_STATIC int sctp_destroy_sock(struct sock *sk)
+{
+	struct sctp_endpoint *ep;
+
+	SCTP_DEBUG_PRINTK("sctp_destroy_sock(sk: %p)\n", sk);
+
+	/* Release our hold on the endpoint. */
+	ep = sctp_sk(sk)->ep;
+	sctp_endpoint_free(ep);
+
+	return 0;
+}
+
+/* API 4.1.7 shutdown() - TCP Style Syntax
+ *     int shutdown(int socket, int how);
+ *
+ *     sd      - the socket descriptor of the association to be closed.
+ *     how     - Specifies the type of shutdown.  The  values  are
+ *               as follows:
+ *               SHUT_RD
+ *                     Disables further receive operations. No SCTP
+ *                     protocol action is taken.
+ *               SHUT_WR
+ *                     Disables further send operations, and initiates
+ *                     the SCTP shutdown sequence.
+ *               SHUT_RDWR
+ *                     Disables further send  and  receive  operations
+ *                     and initiates the SCTP shutdown sequence.
+ */
+SCTP_STATIC void sctp_shutdown(struct sock *sk, int how)
+{
+	struct sctp_endpoint *ep;
+	struct sctp_association *asoc;
+
+	if (SCTP_SOCKET_TCP != sctp_sk(sk)->type)
+		return;
+
+	if (how & SEND_SHUTDOWN) {
+		ep = sctp_sk(sk)->ep;
+		if (!list_empty(&ep->asocs)) {
+			asoc = list_entry(ep->asocs.next,
+					  struct sctp_association, asocs);
+			sctp_primitive_SHUTDOWN(asoc, NULL);
+		}
+	}
+}
+
+/* 7.2.1 Association Status (SCTP_STATUS)
+
+ * Applications can retrieve current status information about an
+ * association, including association state, peer receiver window size,
+ * number of unacked data chunks, and number of data chunks pending
+ * receipt.  This information is read-only.
+ */
+static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
+				       int *optlen)
+{
+	struct sctp_status status;
+	struct sctp_association *asoc = NULL;
+	struct sctp_transport *transport;
+	sctp_assoc_t associd;
+	int retval = 0;
+
+	if (len != sizeof(status)) {
+		retval = -EINVAL;
+		goto out;
+	}
+
+	if (copy_from_user(&status, optval, sizeof(status))) {
+		retval = -EFAULT;
+		goto out;
+	}
+
+	associd = status.sstat_assoc_id;
+	asoc = sctp_id2assoc(sk, associd);
+	if (!asoc) {
+		retval = -EINVAL;
+		goto out;
+	}
+
+	transport = asoc->peer.primary_path;
+
+	status.sstat_assoc_id = sctp_assoc2id(asoc);
+	status.sstat_state = asoc->state;
+	status.sstat_rwnd =  asoc->peer.rwnd;
+	status.sstat_unackdata = asoc->unack_data;
+	status.sstat_penddata = asoc->peer.tsn_map.pending_data;
+	status.sstat_instrms = asoc->c.sinit_max_instreams;
+	status.sstat_outstrms = asoc->c.sinit_num_ostreams;
+	status.sstat_fragmentation_point = asoc->frag_point;
+	status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
+	memcpy(&status.sstat_primary.spinfo_address,
+	       &(transport->ipaddr), sizeof(union sctp_addr));
+	status.sstat_primary.spinfo_state = transport->active;
+	status.sstat_primary.spinfo_cwnd = transport->cwnd;
+	status.sstat_primary.spinfo_srtt = transport->srtt;
+	status.sstat_primary.spinfo_rto = transport->rto;
+	status.sstat_primary.spinfo_mtu = transport->pmtu;
+
+	if (put_user(len, optlen)) {
+		retval = -EFAULT;
+		goto out;
+	}
+
+	SCTP_DEBUG_PRINTK("sctp_getsockopt_sctp_status(%d): %d %d %p\n",
+			  len, status.sstat_state, status.sstat_rwnd,
+			  status.sstat_assoc_id);
+
+	if (copy_to_user(optval, &status, len)) {
+		retval = -EFAULT;
+		goto out;
+	}
+
+out:
+	return (retval);
+}
+
+static int sctp_getsockopt_disable_fragments(struct sock *sk, int len,
+						    char *optval, int *optlen)
+{
+	int val;
+
+	if (len < sizeof(int))
+		return -EINVAL;
+
+	len = sizeof(int);
+	val = (sctp_sk(sk)->disable_fragments == 1);
+	if (put_user(len, optlen))
+		return -EFAULT;
+	if (copy_to_user(optval, &val, len))
+		return -EFAULT;
+	return 0;
+}
+
+static int sctp_getsockopt_set_events(struct sock *sk, int len, char *optval, int *optlen)
+{
+	if (len != sizeof(struct sctp_event_subscribe))
+		return -EINVAL;
+	if (copy_to_user(optval, &sctp_sk(sk)->subscribe, len))
+		return -EFAULT;
+	return 0;
+}
+
+static int sctp_getsockopt_autoclose(struct sock *sk, int len, char *optval, int *optlen)
+{
+	/* Applicable to UDP-style socket only */
+	if (SCTP_SOCKET_TCP == sctp_sk(sk)->type)
+		return -EOPNOTSUPP;
+	if (len != sizeof(int))
+		return -EINVAL;
+	if (copy_to_user(optval, &sctp_sk(sk)->autoclose, len))
+		return -EFAULT;
+	return 0;
+}
+
+/* Helper routine to branch off an association to a new socket.  */
+SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc,
+				struct socket **sockp)
+{
+	struct sock *sk = asoc->base.sk;
+	struct socket *sock;
+	int err = 0;
+
+	/* An association cannot be branched off from an already peeled-off
+	 * socket, nor is this supported for tcp style sockets.
+	 */
+	if (SCTP_SOCKET_UDP != sctp_sk(sk)->type)
+		return -EINVAL;
+
+	/* Create a new socket.  */
+	err = sock_create(sk->family, SOCK_SEQPACKET, IPPROTO_SCTP, &sock);
+	if (err < 0)
+		return err;
+
+	/* Populate the fields of the newsk from the oldsk and migrate the
+	 * asoc to the newsk.
+	 */
+	sctp_sock_migrate(sk, sock->sk, asoc, SCTP_SOCKET_UDP_HIGH_BANDWIDTH);
+	*sockp = sock;
+
+	return err;
+}
+
+static int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval, int *optlen)
+{
+	sctp_peeloff_arg_t peeloff;
+	struct socket *newsock;
+	int retval = 0;
+	struct sctp_association *asoc;
+
+	if (len != sizeof(sctp_peeloff_arg_t))
+		return -EINVAL;
+	if (copy_from_user(&peeloff, optval, len))
+		return -EFAULT;
+
+	asoc = sctp_id2assoc(sk, peeloff.associd);
+	if (!asoc) {
+		retval = -EINVAL;
+		goto out;
+	}
+
+	SCTP_DEBUG_PRINTK("%s: sk: %p asoc: %p\n", __FUNCTION__, sk, asoc);
+
+	retval = sctp_do_peeloff(asoc, &newsock);
+	if (retval < 0)
+		goto out;
+
+	/* Map the socket to an unused fd that can be returned to the user.  */
+	retval = sock_map_fd(newsock);
+	if (retval < 0) {
+		sock_release(newsock);
+		goto out;
+	}
+
+	SCTP_DEBUG_PRINTK("%s: sk: %p asoc: %p newsk: %p sd: %d\n",
+			  __FUNCTION__, sk, asoc, newsock->sk, retval);
+
+	/* Return the fd mapped to the new socket.  */
+	peeloff.sd = retval;
+	if (copy_to_user(optval, &peeloff, len))
+		retval = -EFAULT;
+
+out:
+	return retval;
+}
+
+static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
+						char *optval, int *optlen)
+{
+	struct sctp_paddrparams params;
+	struct sctp_association *asoc;
+	union sctp_addr *addr;
+	struct sctp_transport *trans;
+
+	if (len != sizeof(struct sctp_paddrparams))
+		return -EINVAL;
+	if (copy_from_user(&params, optval, *optlen))
+		return -EFAULT;
+
+	asoc = sctp_id2assoc(sk, params.spp_assoc_id);
+	if (!asoc)
+		return -EINVAL;
+
+	addr = (union sctp_addr *) &(params.spp_address);
+
+	trans = sctp_assoc_lookup_paddr(asoc, addr);
+	if (!trans)
+		return -ENOENT;
+
+	/* The value of the heartbeat interval, in milliseconds. A value of 0,
+	 * when modifying the parameter, specifies that the heartbeat on this
+	 * address should be disabled.
+	 */
+	if (!trans->hb_allowed)
+		params.spp_hbinterval = 0;
+	else
+		params.spp_hbinterval = trans->hb_interval * 1000 / HZ;
+
+	/* spp_pathmaxrxt contains the maximum number of retransmissions
+	 * before this address shall be considered unreachable.
+	 */
+	params.spp_pathmaxrxt = trans->error_threshold;
+
+	if (copy_to_user(optval, &params, len))
+		return -EFAULT;
+	*optlen = len;
+
+	return 0;
+}
+
+static int sctp_getsockopt_initmsg(struct sock *sk, int len, char *optval, int *optlen)
+{
+	if (len != sizeof(struct sctp_initmsg))
+		return -EINVAL;
+	if (copy_to_user(optval, &sctp_sk(sk)->initmsg, len))
+		return -EFAULT;
+	return 0;
+}
+
+static int sctp_getsockopt_peer_addrs_num(struct sock *sk, int len,
+					char *optval, int *optlen)
+{
+	sctp_assoc_t id;
+	struct sctp_association *asoc;
+	struct list_head *pos;
+	int cnt = 0;
+
+	if (len != sizeof(sctp_assoc_t))
+		return -EINVAL;
+
+	if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
+		return -EFAULT;
+
+	/*
+	 *  For UDP-style sockets, id specifies the association to query.
+	 */
+	asoc = sctp_id2assoc(sk, id);
+	if (!asoc)
+		return -EINVAL;
+
+	list_for_each(pos, &asoc->peer.transport_addr_list) {
+		cnt ++;
+	}
+	if (copy_to_user(optval, &cnt, sizeof(int)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
+				char *optval, int *optlen)
+{
+	struct sctp_association *asoc;
+	struct list_head *pos;
+	int cnt = 0;
+	struct sctp_getaddrs getaddrs;
+	struct sctp_transport *from;
+	struct sockaddr_storage *to;
+
+	if (len != sizeof(struct sctp_getaddrs))
+		return -EINVAL;
+
+	if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
+		return -EFAULT;
+
+	if (getaddrs.addr_num <= 0) return -EINVAL;
+	/*
+	 *  For UDP-style sockets, id specifies the association to query.
+	 */
+	asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
+	if (!asoc)
+		return -EINVAL;
+
+	to = getaddrs.addrs;
+	list_for_each(pos, &asoc->peer.transport_addr_list) {
+		from = list_entry(pos, struct sctp_transport, transports);
+		if (copy_to_user(to, &from->ipaddr, sizeof(from->ipaddr)))
+			return -EFAULT;
+		to ++;
+		cnt ++;
+		if (cnt >= getaddrs.addr_num) break;
+	}
+	getaddrs.addr_num = cnt;
+	if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int sctp_getsockopt_local_addrs_num(struct sock *sk, int len,
+						char *optval, int *optlen)
+{
+	sctp_assoc_t id;
+	sctp_bind_addr_t *bp;
+	struct sctp_association *asoc;
+	struct list_head *pos;
+	int cnt = 0;
+
+	if (len != sizeof(sctp_assoc_t))
+		return -EINVAL;
+
+	if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
+		return -EFAULT;
+
+	/*
+	 *  For UDP-style sockets, id specifies the association to query.
+	 *  If the id field is set to the value '0' then the locally bound
+	 *  addresses are returned without regard to any particular
+	 *  association.
+	 */
+	if (0 == id) {
+		bp = &sctp_sk(sk)->ep->base.bind_addr;
+	} else {
+		asoc = sctp_id2assoc(sk, id);
+		if (!asoc)
+			return -EINVAL;
+		bp = &asoc->base.bind_addr;
+	}
+
+	list_for_each(pos, &bp->address_list) {
+		cnt ++;
+	}
+	if (copy_to_user(optval, &cnt, sizeof(int)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
+					char *optval, int *optlen)
+{
+	sctp_bind_addr_t *bp;
+	struct sctp_association *asoc;
+	struct list_head *pos;
+	int cnt = 0;
+	struct sctp_getaddrs getaddrs;
+	struct sockaddr_storage_list *from;
+	struct sockaddr_storage *to;
+
+	if (len != sizeof(struct sctp_getaddrs))
+		return -EINVAL;
+
+	if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
+		return -EFAULT;
+
+	if (getaddrs.addr_num <= 0) return -EINVAL;
+	/*
+	 *  For UDP-style sockets, id specifies the association to query.
+	 *  If the id field is set to the value '0' then the locally bound
+	 *  addresses are returned without regard to any particular
+	 *  association.
+	 */
+	if (0 == getaddrs.assoc_id) {
+		bp = &sctp_sk(sk)->ep->base.bind_addr;
+	} else {
+		asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
+		if (!asoc)
+			return -EINVAL;
+		bp = &asoc->base.bind_addr;
+	}
+
+	to = getaddrs.addrs;
+	list_for_each(pos, &bp->address_list) {
+		from = list_entry(pos,
+				struct sockaddr_storage_list,
+				list);
+		if (copy_to_user(to, &from->a, sizeof(from->a)))
+			return -EFAULT;
+		to ++;
+		cnt ++;
+		if (cnt >= getaddrs.addr_num) break;
+	}
+	getaddrs.addr_num = cnt;
+	if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/* 7.1.10 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR)
+ *
+ * Requests that the local SCTP stack use the enclosed peer address as
+ * the association primary.  The enclosed address must be one of the
+ * association peer's addresses.
+ */
+static int sctp_getsockopt_peer_prim(struct sock *sk, int len,
+				char *optval, int *optlen)
+{
+	struct sctp_setpeerprim prim;
+	struct sctp_association *asoc;
+
+	if (len != sizeof(struct sctp_setpeerprim))
+		return -EINVAL;
+
+	if (copy_from_user(&prim, optval, sizeof(struct sctp_setpeerprim)))
+		return -EFAULT;
+
+	asoc = sctp_id2assoc(sk, prim.sspp_assoc_id);
+	if (!asoc)
+		return -EINVAL;
+
+	if (!asoc->peer.primary_path)
+		return -ENOTCONN;
+
+	memcpy(&prim.sspp_addr, &asoc->peer.primary_path->ipaddr,
+	       sizeof(union sctp_addr));
+
+	if (copy_to_user(optval, &prim, sizeof(struct sctp_setpeerprim)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/*
+ *
+ * 7.1.15 Set default send parameters (SET_DEFAULT_SEND_PARAM)
+ *
+ *   Applications that wish to use the sendto() system call may wish to
+ *   specify a default set of parameters that would normally be supplied
+ *   through the inclusion of ancillary data.  This socket option allows
+ *   such an application to set the default sctp_sndrcvinfo structure.
+ *   The application that wishes to use this socket option simply passes
+ *   in to this call the sctp_sndrcvinfo structure defined in Section
+ *   5.2.2) The input parameters accepted by this call include
+ *   sinfo_stream, sinfo_flags, sinfo_ppid, sinfo_context,
+ *   sinfo_timetolive.  The user must provide the sinfo_assoc_id field in
+ *   to this call if the caller is using the UDP model.
+ *
+ *   For getsockopt, it get the default sctp_sndrcvinfo structure.
+ */
+static int sctp_getsockopt_default_send_param(struct sock *sk,
+					int len, char *optval, int *optlen)
+{
+	struct sctp_sndrcvinfo info;
+	struct sctp_association *asoc;
+
+	if (len != sizeof(struct sctp_sndrcvinfo))
+		return -EINVAL;
+	if (copy_from_user(&info, optval, sizeof(struct sctp_sndrcvinfo)))
+		return -EFAULT;
+
+	asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
+	if (!asoc)
+		return -EINVAL;
+
+	info.sinfo_stream = asoc->defaults.stream;
+	info.sinfo_flags = asoc->defaults.flags;
+	info.sinfo_ppid = asoc->defaults.ppid;
+	info.sinfo_context = asoc->defaults.context;
+	info.sinfo_timetolive = asoc->defaults.timetolive;
+
+	if (copy_to_user(optval, &info, sizeof(struct sctp_sndrcvinfo)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/*
+ *
+ * 7.1.5 SCTP_NODELAY
+ *
+ * Turn on/off any Nagle-like algorithm.  This means that packets are
+ * generally sent as soon as possible and no unnecessary delays are
+ * introduced, at the cost of more packets in the network.  Expects an
+ * integer boolean flag.
+ */
+
+static int sctp_getsockopt_nodelay(struct sock *sk, int len,
+					char *optval, int *optlen)
+{
+	__u8 val;
+
+	if (len < sizeof(__u8))
+		return -EINVAL;
+
+	len = sizeof(__u8);
+	val = (sctp_sk(sk)->nodelay == 1);
+	if (put_user(len, optlen))
+		return -EFAULT;
+	if (copy_to_user(optval, &val, len))
+		return -EFAULT;
+	return 0;
+}
+
+SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
+				char *optval, int *optlen)
+{
+	int retval = 0;
+	int len;
+
+	SCTP_DEBUG_PRINTK("sctp_getsockopt(sk: %p, ...)\n", sk);
+
+	/* I can hardly begin to describe how wrong this is.  This is
+	 * so broken as to be worse than useless.  The API draft
+	 * REALLY is NOT helpful here...  I am not convinced that the
+	 * semantics of getsockopt() with a level OTHER THAN SOL_SCTP
+	 * are at all well-founded.
+	 */
+	if (level != SOL_SCTP) {
+		struct sctp_af *af = sctp_sk(sk)->pf->af;
+
+		retval = af->getsockopt(sk, level, optname, optval, optlen);
+		return retval;
+	}
+
+	if (get_user(len, optlen))
+		return -EFAULT;
+
+	sctp_lock_sock(sk);
+
+	switch (optname) {
+	case SCTP_STATUS:
+		retval = sctp_getsockopt_sctp_status(sk, len, optval, optlen);
+		break;
+	case SCTP_DISABLE_FRAGMENTS:
+		retval = sctp_getsockopt_disable_fragments(sk, len, optval,
+							   optlen);
+		break;
+	case SCTP_SET_EVENTS:
+		retval = sctp_getsockopt_set_events(sk, len, optval, optlen);
+		break;
+	case SCTP_AUTOCLOSE:
+		retval = sctp_getsockopt_autoclose(sk, len, optval, optlen);
+		break;
+	case SCTP_SOCKOPT_PEELOFF:
+		retval = sctp_getsockopt_peeloff(sk, len, optval, optlen);
+		break;
+	case SCTP_GET_PEER_ADDR_PARAMS:
+		retval = sctp_getsockopt_peer_addr_params(sk, len, optval,
+							  optlen);
+		break;
+	case SCTP_INITMSG:
+		retval = sctp_getsockopt_initmsg(sk, len, optval, optlen);
+		break;
+	case SCTP_GET_PEER_ADDRS_NUM:
+		retval = sctp_getsockopt_peer_addrs_num(sk, len, optval,
+							optlen);
+		break;
+	case SCTP_GET_LOCAL_ADDRS_NUM:
+		retval = sctp_getsockopt_local_addrs_num(sk, len, optval,
+							 optlen);
+		break;
+	case SCTP_GET_PEER_ADDRS:
+		retval = sctp_getsockopt_peer_addrs(sk, len, optval,
+						    optlen);
+		break;
+	case SCTP_GET_LOCAL_ADDRS:
+		retval = sctp_getsockopt_local_addrs(sk, len, optval,
+						     optlen);
+		break;
+	case SCTP_SET_DEFAULT_SEND_PARAM:
+		retval = sctp_getsockopt_default_send_param(sk, len,
+							    optval, optlen);
+		break;
+	case SCTP_SET_PEER_PRIMARY_ADDR:
+		retval = sctp_getsockopt_peer_prim(sk, len, optval, optlen);
+		break;
+	case SCTP_NODELAY:
+		retval = sctp_getsockopt_nodelay(sk, len, optval, optlen);
+		break;
+	default:
+		retval = -ENOPROTOOPT;
+		break;
+	};
+
+	sctp_release_sock(sk);
+	return retval;
+}
+
+static void sctp_hash(struct sock *sk)
+{
+	/* STUB */
+}
+
+static void sctp_unhash(struct sock *sk)
+{
+	/* STUB */
+}
+
+/* Check if port is acceptable.  Possibly find first available port.
+ *
+ * The port hash table (contained in the 'global' SCTP protocol storage
+ * returned by struct sctp_protocol *sctp_get_protocol()). The hash
+ * table is an array of 4096 lists (sctp_bind_hashbucket_t). Each
+ * list (the list number is the port number hashed out, so as you
+ * would expect from a hash function, all the ports in a given list have
+ * such a number that hashes out to the same list number; you were
+ * expecting that, right?); so each list has a set of ports, with a
+ * link to the socket (struct sock) that uses it, the port number and
+ * a fastreuse flag (FIXME: NPI ipg).
+ */
+static sctp_bind_bucket_t *sctp_bucket_create(sctp_bind_hashbucket_t *head,
+					      unsigned short snum);
+static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
+{
+	sctp_bind_hashbucket_t *head; /* hash list */
+	sctp_bind_bucket_t *pp; /* hash list port iterator */
+	struct sctp_protocol *sctp = sctp_get_protocol();
+	unsigned short snum;
+	int ret;
+
+	/* NOTE:  Remember to put this back to net order. */
+	addr->v4.sin_port = ntohs(addr->v4.sin_port);
+	snum = addr->v4.sin_port;
+
+	SCTP_DEBUG_PRINTK("sctp_get_port() begins, snum=%d\n", snum);
+
+	sctp_local_bh_disable();
+
+	if (snum == 0) {
+		/* Search for an available port.
+		 *
+		 * 'sctp->port_rover' was the last port assigned, so
+		 * we start to search from 'sctp->port_rover +
+		 * 1'. What we do is first check if port 'rover' is
+		 * already in the hash table; if not, we use that; if
+		 * it is, we try next.
+		 */
+		int low = sysctl_local_port_range[0];
+		int high = sysctl_local_port_range[1];
+		int remaining = (high - low) + 1;
+		int rover;
+		int index;
+
+		sctp_spin_lock(&sctp->port_alloc_lock);
+		rover = sctp->port_rover;
+		do {
+			rover++;
+			if ((rover < low) || (rover > high))
+				rover = low;
+			index = sctp_phashfn(rover);
+			head = &sctp->port_hashtable[index];
+			sctp_spin_lock(&head->lock);
+			for (pp = head->chain; pp; pp = pp->next)
+				if (pp->port == rover)
+					goto next;
+			break;
+		next:
+			sctp_spin_unlock(&head->lock);
+		} while (--remaining > 0);
+		sctp->port_rover = rover;
+		sctp_spin_unlock(&sctp->port_alloc_lock);
+
+		/* Exhausted local port range during search? */
+		ret = 1;
+		if (remaining <= 0)
+			goto fail;
+
+		/* OK, here is the one we will use.  HEAD (the port
+		 * hash table list entry) is non-NULL and we hold it's
+		 * mutex.
+		 */
+		snum = rover;
+		pp = NULL;
+	} else {
+		/* We are given an specific port number; we verify
+		 * that it is not being used. If it is used, we will
+		 * exahust the search in the hash list corresponding
+		 * to the port number (snum) - we detect that with the
+		 * port iterator, pp being NULL.
+		 */
+		head = &sctp->port_hashtable[sctp_phashfn(snum)];
+		sctp_spin_lock(&head->lock);
+		for (pp = head->chain; pp; pp = pp->next) {
+			if (pp->port == snum)
+				break;
+		}
+	}
+
+
+	if (pp && pp->sk) {
+		/* We had a port hash table hit - there is an
+		 * available port (pp != NULL) and it is being
+		 * used by other socket (pp->sk != NULL); that other
+		 * socket is going to be sk2.
+		 */
+		int sk_reuse = sk->reuse;
+		struct sock *sk2 = pp->sk;
+
+		SCTP_DEBUG_PRINTK("sctp_get_port() found a "
+				  "possible match\n");
+		if (pp->fastreuse != 0 && sk->reuse != 0)
+			goto success;
+
+		/* Run through the list of sockets bound to the port
+		 * (pp->port) [via the pointers bind_next and
+		 * bind_pprev in the struct sock *sk2 (pp->sk)]. On each one,
+		 * we get the endpoint they describe and run through
+		 * the endpoint's list of IP (v4 or v6) addresses,
+		 * comparing each of the addresses with the address of
+		 * the socket sk. If we find a match, then that means
+		 * that this port/socket (sk) combination are already
+		 * in an endpoint.
+		 */
+		for ( ; sk2 != NULL; sk2 = sk2->bind_next) {
+			struct sctp_endpoint *ep2;
+			ep2 = sctp_sk(sk2)->ep;
+
+			if (sk_reuse && sk2->reuse)
+				continue;
+
+			if (sctp_bind_addr_match(&ep2->base.bind_addr, addr,
+						 sctp_sk(sk)))
+				goto found;
+		}
+
+	found:
+		/* If we found a conflict, fail.  */
+		if (sk2 != NULL) {
+			ret = (long) sk2;
+			goto fail_unlock;
+		}
+		SCTP_DEBUG_PRINTK("sctp_get_port(): Found a match\n");
+	}
+
+	/* If there was a hash table miss, create a new port.  */
+	ret = 1;
+
+	if (!pp && !(pp = sctp_bucket_create(head, snum)))
+		goto fail_unlock;
+
+	/* In either case (hit or miss), make sure fastreuse is 1 only
+	 * if sk->reuse is too (that is, if the caller requested
+	 * SO_REUSEADDR on this socket -sk-).
+	 */
+	if (!pp->sk)
+		pp->fastreuse = sk->reuse ? 1 : 0;
+	else if (pp->fastreuse && sk->reuse == 0)
+		pp->fastreuse = 0;
+
+	/* We are set, so fill up all the data in the hash table
+	 * entry, tie the socket list information with the rest of the
+	 * sockets FIXME: Blurry, NPI (ipg).
+	 */
+success:
+	(sk)->num = snum;
+	if (sk->prev == NULL) {
+		if ((sk->bind_next = pp->sk) != NULL)
+			pp->sk->bind_pprev = &sk->bind_next;
+		pp->sk = sk;
+		sk->bind_pprev = &pp->sk;
+		sk->prev = (struct sock *) pp;
+	}
+	ret = 0;
+
+fail_unlock:
+	sctp_spin_unlock(&head->lock);
+
+fail:
+	sctp_local_bh_enable();
+
+	SCTP_DEBUG_PRINTK("sctp_get_port() ends, ret=%d\n", ret);
+	addr->v4.sin_port = htons(addr->v4.sin_port);
+	return ret;
+}
+
+/* Assign a 'snum' port to the socket.  If snum == 0, an ephemeral
+ * port is requested.
+ */
+static int sctp_get_port(struct sock *sk, unsigned short snum)
+{
+	long ret;
+	union sctp_addr addr;
+	struct sctp_af *af = sctp_sk(sk)->pf->af;
+
+	/* Set up a dummy address struct from the sk. */
+	af->from_sk(&addr, sk);
+	addr.v4.sin_port = htons(snum);
+
+	/* Note: sk->num gets filled in if ephemeral port request. */
+	ret = sctp_get_port_local(sk, &addr);
+
+	return (ret ? 1 : 0);
+}
+
+/*
+ * 3.1.3 listen() - UDP Style Syntax
+ *
+ *   By default, new associations are not accepted for UDP style sockets.
+ *   An application uses listen() to mark a socket as being able to
+ *   accept new associations.
+ */
+SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog)
+{
+	struct sctp_opt *sp = sctp_sk(sk);
+	struct sctp_endpoint *ep = sp->ep;
+
+	/* Only UDP style sockets that are not peeled off are allowed to
+	 * listen().
+	 */
+	if (SCTP_SOCKET_UDP != sp->type)
+		return -EINVAL;
+
+	if (sk->state == SCTP_SS_LISTENING)
+		return 0;
+
+	/*
+	 * If a bind() or sctp_bindx() is not called prior to a listen()
+	 * call that allows new associations to be accepted, the system
+	 * picks an ephemeral port and will choose an address set equivalent
+	 * to binding with a wildcard address.
+	 *
+	 * This is not currently spelled out in the SCTP sockets
+	 * extensions draft, but follows the practice as seen in TCP
+	 * sockets.
+	 */
+	if (!ep->base.bind_addr.port) {
+		if (sctp_autobind(sk))
+			return -EAGAIN;
+	}
+	sk->state = SCTP_SS_LISTENING;
+	sctp_hash_endpoint(ep);
+	return 0;
+}
+
+/*
+ * 4.1.3 listen() - TCP Style Syntax
+ *
+ *   Applications uses listen() to ready the SCTP endpoint for accepting
+ *   inbound associations.
+ */
+SCTP_STATIC int sctp_stream_listen(struct sock *sk, int backlog)
+{
+	struct sctp_opt *sp = sctp_sk(sk);
+	struct sctp_endpoint *ep = sp->ep;
+
+	if (sk->state == SCTP_SS_LISTENING)
+		return 0;
+
+	/*
+	 * If a bind() or sctp_bindx() is not called prior to a listen()
+	 * call that allows new associations to be accepted, the system
+	 * picks an ephemeral port and will choose an address set equivalent
+	 * to binding with a wildcard address.
+	 *
+	 * This is not currently spelled out in the SCTP sockets
+	 * extensions draft, but follows the practice as seen in TCP
+	 * sockets.
+	 */
+	if (!ep->base.bind_addr.port) {
+		if (sctp_autobind(sk))
+			return -EAGAIN;
+	}
+	sk->state = SCTP_SS_LISTENING;
+	sk->max_ack_backlog = backlog;
+	sctp_hash_endpoint(ep);
+	return 0;
+}
+
+/*
+ *  Move a socket to LISTENING state.
+ */
+int sctp_inet_listen(struct socket *sock, int backlog)
+{
+	struct sock *sk = sock->sk;
+	struct crypto_tfm *tfm=NULL;
+	int err = -EINVAL;
+
+	if (unlikely(backlog < 0))
+		goto out;
+
+	sctp_lock_sock(sk);
+
+	if (sock->state != SS_UNCONNECTED)
+		goto out;
+
+	/* Allocate HMAC for generating cookie. */
+	if (sctp_hmac_alg) {
+		tfm = sctp_crypto_alloc_tfm(sctp_hmac_alg, 0);
+		if (!tfm) {
+			err = -ENOSYS;
+			goto out;
+		}
+	}
+
+	switch (sock->type) {
+	case SOCK_SEQPACKET:
+		err = sctp_seqpacket_listen(sk, backlog);
+		break;
+	case SOCK_STREAM:
+		err = sctp_stream_listen(sk, backlog);
+		break;
+	default:
+		break;
+	};
+	if (err)
+		goto cleanup;
+
+	/* Store away the transform reference. */
+	sctp_sk(sk)->hmac = tfm;
+out:
+	sctp_release_sock(sk);
+	return err;
+cleanup:
+	if (tfm)
+		sctp_crypto_free_tfm(tfm);
+	goto out;
+}
+
+/*
+ * This function is done by modeling the current datagram_poll() and the
+ * tcp_poll().  Note that, based on these implementations, we don't
+ * lock the socket in this function, even though it seems that,
+ * ideally, locking or some other mechanisms can be used to ensure
+ * the integrity of the counters (sndbuf and wmem_queued) used
+ * in this place.  We assume that we don't need locks either until proven
+ * otherwise.
+ *
+ * Another thing to note is that we include the Async I/O support
+ * here, again, by modeling the current TCP/UDP code.  We don't have
+ * a good way to test with it yet.
+ */
+unsigned int sctp_poll(struct file *file, struct socket *sock, poll_table *wait)
+{
+	struct sock *sk = sock->sk;
+	unsigned int mask;
+
+	poll_wait(file, sk->sleep, wait);
+	mask = 0;
+
+	/* Is there any exceptional events?  */
+	if (sk->err || !skb_queue_empty(&sk->error_queue))
+		mask |= POLLERR;
+	if (sk->shutdown == SHUTDOWN_MASK)
+		mask |= POLLHUP;
+
+	/* Is it readable?  Reconsider this code with TCP-style support.  */
+	if (!skb_queue_empty(&sk->receive_queue) ||
+	    (sk->shutdown & RCV_SHUTDOWN))
+		mask |= POLLIN | POLLRDNORM;
+
+	/*
+	 * FIXME: We need to set SCTP_SS_DISCONNECTING for TCP-style and
+	 * peeled off sockets.  Additionally, TCP-style needs to consider
+	 * other establishment conditions.
+	 */
+	if (SCTP_SOCKET_UDP != sctp_sk(sk)->type) {
+		/* The association is going away.  */
+		if (SCTP_SS_DISCONNECTING == sk->state)
+			mask |= POLLHUP;
+		/* The association is either gone or not ready.  */
+		if (SCTP_SS_CLOSED == sk->state)
+			return mask;
+	}
+
+	/* Is it writable?  */
+	if (sctp_writeable(sk)) {
+		mask |= POLLOUT | POLLWRNORM;
+	} else {
+		set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
+		/*
+		 * Since the socket is not locked, the buffer
+		 * might be made available after the writeable check and
+		 * before the bit is set.  This could cause a lost I/O
+		 * signal.  tcp_poll() has a race breaker for this race
+		 * condition.  Based on their implementation, we put
+		 * in the following code to cover it as well.
+		 */
+		if (sctp_writeable(sk))
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	return mask;
+}
+
+/********************************************************************
+ * 2nd Level Abstractions
+ ********************************************************************/
+
+static sctp_bind_bucket_t *sctp_bucket_create(sctp_bind_hashbucket_t *head, unsigned short snum)
+{
+	sctp_bind_bucket_t *pp;
+
+	SCTP_DEBUG_PRINTK( "sctp_bucket_create() begins, snum=%d\n", snum);
+	pp = kmalloc(sizeof(sctp_bind_bucket_t), GFP_ATOMIC);
+	if (pp) {
+		pp->port = snum;
+		pp->fastreuse = 0;
+		pp->sk = NULL;
+		if ((pp->next = head->chain) != NULL)
+			pp->next->pprev = &pp->next;
+		head->chain = pp;
+		pp->pprev = &head->chain;
+	}
+	SCTP_DEBUG_PRINTK("sctp_bucket_create() ends, pp=%p\n", pp);
+	return pp;
+}
+
+/* FIXME: Comments! */
+static __inline__ void __sctp_put_port(struct sock *sk)
+{
+	struct sctp_protocol *sctp_proto = sctp_get_protocol();
+	sctp_bind_hashbucket_t *head =
+		&sctp_proto->port_hashtable[sctp_phashfn((sk)->num)];
+	sctp_bind_bucket_t *pp;
+
+	sctp_spin_lock(&head->lock);
+	pp = (sctp_bind_bucket_t *) sk->prev;
+	if (sk->bind_next)
+		sk->bind_next->bind_pprev = sk->bind_pprev;
+	*(sk->bind_pprev) = sk->bind_next;
+	sk->prev = NULL;
+	(sk)->num = 0;
+	if (pp->sk) {
+		if (pp->next)
+			pp->next->pprev = pp->pprev;
+		*(pp->pprev) = pp->next;
+		kfree(pp);
+	}
+	sctp_spin_unlock(&head->lock);
+}
+
+void sctp_put_port(struct sock *sk)
+{
+	sctp_local_bh_disable();
+	__sctp_put_port(sk);
+	sctp_local_bh_enable();
+}
+
+/*
+ * The system picks an ephemeral port and choose an address set equivalent
+ * to binding with a wildcard address.
+ * One of those addresses will be the primary address for the association.
+ * This automatically enables the multihoming capability of SCTP.
+ */
+static int sctp_autobind(struct sock *sk)
+{
+	union sctp_addr autoaddr;
+	struct sctp_af *af;
+	unsigned short port;
+
+	/* Initialize a local sockaddr structure to INADDR_ANY. */
+	af = sctp_sk(sk)->pf->af;
+
+	port = htons((sk)->num);
+	af->inaddr_any(&autoaddr, port);
+
+	return sctp_do_bind(sk, &autoaddr, af->sockaddr_len);
+}
+
+/* Parse out IPPROTO_SCTP CMSG headers.  Perform only minimal validation.
+ *
+ * From RFC 2292
+ * 4.2 The cmsghdr Structure *
+ *
+ * When ancillary data is sent or received, any number of ancillary data
+ * objects can be specified by the msg_control and msg_controllen members of
+ * the msghdr structure, because each object is preceded by
+ * a cmsghdr structure defining the object's length (the cmsg_len member).
+ * Historically Berkeley-derived implementations have passed only one object
+ * at a time, but this API allows multiple objects to be
+ * passed in a single call to sendmsg() or recvmsg(). The following example
+ * shows two ancillary data objects in a control buffer.
+ *
+ *   |<--------------------------- msg_controllen -------------------------->|
+ *   |                                                                       |
+ *
+ *   |<----- ancillary data object ----->|<----- ancillary data object ----->|
+ *
+ *   |<---------- CMSG_SPACE() --------->|<---------- CMSG_SPACE() --------->|
+ *   |                                   |                                   |
+ *
+ *   |<---------- cmsg_len ---------->|  |<--------- cmsg_len ----------->|  |
+ *
+ *   |<--------- CMSG_LEN() --------->|  |<-------- CMSG_LEN() ---------->|  |
+ *   |                                |  |                                |  |
+ *
+ *   +-----+-----+-----+--+-----------+--+-----+-----+-----+--+-----------+--+
+ *   |cmsg_|cmsg_|cmsg_|XX|           |XX|cmsg_|cmsg_|cmsg_|XX|           |XX|
+ *
+ *   |len  |level|type |XX|cmsg_data[]|XX|len  |level|type |XX|cmsg_data[]|XX|
+ *
+ *   +-----+-----+-----+--+-----------+--+-----+-----+-----+--+-----------+--+
+ *    ^
+ *    |
+ *
+ * msg_control
+ * points here
+ */
+SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *msg,
+				  sctp_cmsgs_t *cmsgs)
+{
+	struct cmsghdr *cmsg;
+
+	for (cmsg = CMSG_FIRSTHDR(msg);
+	     cmsg != NULL;
+	     cmsg = CMSG_NXTHDR((struct msghdr*)msg, cmsg)) {
+		/* Check for minimum length.  The SCM code has this check.  */
+		if (cmsg->cmsg_len < sizeof(struct cmsghdr) ||
+		    (unsigned long)(((char*)cmsg - (char*)msg->msg_control)
+				    + cmsg->cmsg_len) > msg->msg_controllen) {
+			return -EINVAL;
+		}
+
+		/* Should we parse this header or ignore?  */
+		if (cmsg->cmsg_level != IPPROTO_SCTP)
+			continue;
+
+		/* Strictly check lengths following example in SCM code.  */
+		switch (cmsg->cmsg_type) {
+		case SCTP_INIT:
+			/* SCTP Socket API Extension
+			 * 5.2.1 SCTP Initiation Structure (SCTP_INIT)
+			 *
+			 * This cmsghdr structure provides information for
+			 * initializing new SCTP associations with sendmsg().
+			 * The SCTP_INITMSG socket option uses this same data
+			 * structure.  This structure is not used for
+			 * recvmsg().
+			 *
+			 * cmsg_level    cmsg_type      cmsg_data[]
+			 * ------------  ------------   ----------------------
+			 * IPPROTO_SCTP  SCTP_INIT      struct sctp_initmsg
+			 */
+			if (cmsg->cmsg_len !=
+			    CMSG_LEN(sizeof(struct sctp_initmsg)))
+				return -EINVAL;
+			cmsgs->init = (struct sctp_initmsg *)CMSG_DATA(cmsg);
+			break;
+
+		case SCTP_SNDRCV:
+			/* SCTP Socket API Extension
+			 * 5.2.2 SCTP Header Information Structure(SCTP_SNDRCV)
+			 *
+			 * This cmsghdr structure specifies SCTP options for
+			 * sendmsg() and describes SCTP header information
+			 * about a received message through recvmsg().
+			 *
+			 * cmsg_level    cmsg_type      cmsg_data[]
+			 * ------------  ------------   ----------------------
+			 * IPPROTO_SCTP  SCTP_SNDRCV    struct sctp_sndrcvinfo
+			 */
+			if (cmsg->cmsg_len !=
+			    CMSG_LEN(sizeof(struct sctp_sndrcvinfo)))
+				return -EINVAL;
+
+			cmsgs->info =
+				(struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
+
+			/* Minimally, validate the sinfo_flags. */
+			if (cmsgs->info->sinfo_flags &
+			    ~(MSG_UNORDERED | MSG_ADDR_OVER |
+			      MSG_ABORT | MSG_EOF))
+				return -EINVAL;
+			break;
+
+		default:
+			return -EINVAL;
+		};
+	}
+	return 0;
+}
+
+/*
+ * Wait for a packet..
+ * Note: This function is the same function as in core/datagram.c
+ * with a few modifications to make lksctp work.
+ */
+static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p)
+{
+	int error;
+	DECLARE_WAITQUEUE(wait, current);
+
+	__set_current_state(TASK_INTERRUPTIBLE);
+	add_wait_queue_exclusive(sk->sleep, &wait);
+
+	/* Socket errors? */
+	error = sock_error(sk);
+	if (error)
+		goto out;
+
+	if (!skb_queue_empty(&sk->receive_queue))
+		goto ready;
+
+	/* Socket shut down?  */
+	if (sk->shutdown & RCV_SHUTDOWN)
+		goto out;
+
+	/* Sequenced packets can come disconnected.  If so we report the
+	 * problem.
+	 */
+	error = -ENOTCONN;
+
+	/* Is there a good reason to think that we may receive some data?  */
+	if ((list_empty(&sctp_sk(sk)->ep->asocs)) &&
+	    (sk->state != SCTP_SS_LISTENING))
+		goto out;
+
+	/* Handle signals.  */
+	if (signal_pending(current))
+		goto interrupted;
+
+	/* Let another process have a go.  Since we are going to sleep
+	 * anyway.  Note: This may cause odd behaviors if the message
+	 * does not fit in the user's buffer, but this seems to be the
+	 * only way to honor MSG_DONTWAIT realistically.
+	 */
+	sctp_release_sock(sk);
+	*timeo_p = schedule_timeout(*timeo_p);
+	sctp_lock_sock(sk);
+
+ready:
+	remove_wait_queue(sk->sleep, &wait);
+	__set_current_state(TASK_RUNNING);
+	return 0;
+
+interrupted:
+	error = sock_intr_errno(*timeo_p);
+
+out:
+	remove_wait_queue(sk->sleep, &wait);
+	__set_current_state(TASK_RUNNING);
+	*err = error;
+	return error;
+}
+
+/* Receive a datagram.
+ * Note: This is pretty much the same routine as in core/datagram.c
+ * with a few changes to make lksctp work.
+ */
+static struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags,
+					      int noblock, int *err)
+{
+	int error;
+	struct sk_buff *skb;
+	long timeo;
+
+	/* Caller is allowed not to check sk->err before calling.  */
+	error = sock_error(sk);
+	if (error)
+		goto no_packet;
+
+	timeo = sock_rcvtimeo(sk, noblock);
+
+	SCTP_DEBUG_PRINTK("Timeout: timeo: %ld, MAX: %ld.\n",
+			  timeo, MAX_SCHEDULE_TIMEOUT);
+
+	do {
+		/* Again only user level code calls this function,
+		 * so nothing interrupt level
+		 * will suddenly eat the receive_queue.
+		 *
+		 *  Look at current nfs client by the way...
+		 *  However, this function was corrent in any case. 8)
+		 */
+		if (flags & MSG_PEEK) {
+			unsigned long cpu_flags;
+
+			sctp_spin_lock_irqsave(&sk->receive_queue.lock,
+					       cpu_flags);
+			skb = skb_peek(&sk->receive_queue);
+			if (skb)
+				atomic_inc(&skb->users);
+			sctp_spin_unlock_irqrestore(&sk->receive_queue.lock,
+						    cpu_flags);
+		} else {
+			skb = skb_dequeue(&sk->receive_queue);
+		}
+
+		if (skb)
+			return skb;
+
+		if (sk->shutdown & RCV_SHUTDOWN)
+			break;
+
+		/* User doesn't want to wait.  */
+		error = -EAGAIN;
+		if (!timeo)
+			goto no_packet;
+	} while (sctp_wait_for_packet(sk, err, &timeo) == 0);
+
+	return NULL;
+
+no_packet:
+	*err = error;
+	return NULL;
+}
+
+/* Verify that this is a valid address. */
+static inline int sctp_verify_addr(struct sock *sk, union sctp_addr *addr,
+				   int len)
+{
+	struct sctp_af *af;
+
+	/* Verify basic sockaddr. */
+	af = sctp_sockaddr_af(sctp_sk(sk), addr, len);
+	if (!af)
+		return -EINVAL;
+
+	/* Is this a valid SCTP address?  */
+	if (!af->addr_valid(addr))
+		return -EINVAL;
+
+	if (!sctp_sk(sk)->pf->send_verify(sctp_sk(sk), (addr)))
+		return -EINVAL;
+
+	return 0;
+}
+
+/* Get the sndbuf space available at the time on the association.  */
+static inline int sctp_wspace(struct sctp_association *asoc)
+{
+	struct sock *sk = asoc->base.sk;
+	int amt = 0;
+
+	amt = sk->sndbuf - asoc->sndbuf_used;
+	if (amt < 0)
+		amt = 0;
+	return amt;
+}
+
+/* Increment the used sndbuf space count of the corresponding association by
+ * the size of the outgoing data chunk.
+ * Also, set the skb destructor for sndbuf accounting later.
+ *
+ * Since it is always 1-1 between chunk and skb, and also a new skb is always
+ * allocated for chunk bundling in sctp_packet_transmit(), we can use the
+ * destructor in the data chunk skb for the purpose of the sndbuf space
+ * tracking.
+ */
+static inline void sctp_set_owner_w(sctp_chunk_t *chunk)
+{
+	struct sctp_association *asoc = chunk->asoc;
+	struct sock *sk = asoc->base.sk;
+
+	/* The sndbuf space is tracked per association.  */
+	sctp_association_hold(asoc);
+
+	chunk->skb->destructor = sctp_wfree;
+	/* Save the chunk pointer in skb for sctp_wfree to use later.  */
+	*((sctp_chunk_t **)(chunk->skb->cb)) = chunk;
+
+	asoc->sndbuf_used += SCTP_DATA_SNDSIZE(chunk);
+	sk->wmem_queued += SCTP_DATA_SNDSIZE(chunk);
+}
+
+/* If sndbuf has changed, wake up per association sndbuf waiters.  */
+static void __sctp_write_space(struct sctp_association *asoc)
+{
+	struct sock *sk = asoc->base.sk;
+	struct socket *sock = sk->socket;
+
+	if ((sctp_wspace(asoc) > 0) && sock) {
+		if (waitqueue_active(&asoc->wait))
+			wake_up_interruptible(&asoc->wait);
+
+		if (sctp_writeable(sk)) {
+			if (sk->sleep && waitqueue_active(sk->sleep))
+				wake_up_interruptible(sk->sleep);
+
+			/* Note that we try to include the Async I/O support
+			 * here by modeling from the current TCP/UDP code.
+			 * We have not tested with it yet.
+			 */
+			if (sock->fasync_list &&
+			    !(sk->shutdown & SEND_SHUTDOWN))
+				sock_wake_async(sock, 2, POLL_OUT);
+		}
+	}
+}
+
+/* Do accounting for the sndbuf space.
+ * Decrement the used sndbuf space of the corresponding association by the
+ * data size which was just transmitted(freed).
+ */
+static void sctp_wfree(struct sk_buff *skb)
+{
+	struct sctp_association *asoc;
+	sctp_chunk_t *chunk;
+	struct sock *sk;
+
+	/* Get the saved chunk pointer.  */
+	chunk = *((sctp_chunk_t **)(skb->cb));
+	asoc = chunk->asoc;
+	sk = asoc->base.sk;
+	asoc->sndbuf_used -= SCTP_DATA_SNDSIZE(chunk);
+	sk->wmem_queued -= SCTP_DATA_SNDSIZE(chunk);
+	__sctp_write_space(asoc);
+
+	sctp_association_put(asoc);
+}
+
+/* Helper function to wait for space in the sndbuf.  */
+static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
+				int msg_len)
+{
+	struct sock *sk = asoc->base.sk;
+	int err = 0;
+	long current_timeo = *timeo_p;
+	DECLARE_WAITQUEUE(wait, current);
+
+	SCTP_DEBUG_PRINTK("wait_for_sndbuf: asoc=%p, timeo=%ld, msg_len=%d\n",
+	                  asoc, (long)(*timeo_p), msg_len);
+
+	/* Wait on the association specific sndbuf space. */
+	add_wait_queue_exclusive(&asoc->wait, &wait);
+
+	/* Increment the association's refcnt.  */
+	sctp_association_hold(asoc);
+	for (;;) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (!*timeo_p)
+			goto do_nonblock;
+		if (sk->err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING ||
+		    asoc->base.dead)
+			goto do_error;
+		if (signal_pending(current))
+			goto do_interrupted;
+		if (msg_len <= sctp_wspace(asoc))
+			break;
+
+		/* Let another process have a go.  Since we are going
+		 * to sleep anyway.
+		 */
+		sctp_release_sock(sk);
+		current_timeo = schedule_timeout(current_timeo);
+		sctp_lock_sock(sk);
+
+		*timeo_p = current_timeo;
+	}
+
+out:
+	remove_wait_queue(&asoc->wait, &wait);
+
+	/* Release the association's refcnt.  */
+	sctp_association_put(asoc);
+
+	__set_current_state(TASK_RUNNING);
+	return err;
+
+do_error:
+	err = -EPIPE;
+	goto out;
+
+do_interrupted:
+	err = sock_intr_errno(*timeo_p);
+	goto out;
+
+do_nonblock:
+	err = -EAGAIN;
+	goto out;
+}
+
+/* If socket sndbuf has changed, wake up all per association waiters.  */
+void sctp_write_space(struct sock *sk)
+{
+	struct sctp_association *asoc;
+	struct list_head *pos;
+
+	/* Wake up the tasks in each wait queue.  */
+	list_for_each(pos, &((sctp_sk(sk))->ep->asocs)) {
+		asoc = list_entry(pos, struct sctp_association, asocs);
+		__sctp_write_space(asoc);
+	}
+}
+
+/* Is there any sndbuf space available on the socket?
+ *
+ * Note that wmem_queued is the sum of the send buffers on all of the
+ * associations on the same socket.  For a UDP-style socket with
+ * multiple associations, it is possible for it to be "unwriteable"
+ * prematurely.  I assume that this is acceptable because
+ * a premature "unwriteable" is better than an accidental "writeable" which
+ * would cause an unwanted block under certain circumstances.  For the 1-1
+ * UDP-style sockets or TCP-style sockets, this code should work.
+ *  - Daisy
+ */
+static int sctp_writeable(struct sock *sk)
+{
+	int amt = 0;
+
+	amt = sk->sndbuf - sk->wmem_queued;
+	if (amt < 0)
+		amt = 0;
+	return amt;
+}
+
+/* Wait for an association to go into ESTABLISHED state. If timeout is 0,
+ * returns immediately with EINPROGRESS.
+ */
+static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p)
+{
+	struct sock *sk = asoc->base.sk;
+	int err = 0;
+	long current_timeo = *timeo_p;
+	DECLARE_WAITQUEUE(wait, current);
+
+	SCTP_DEBUG_PRINTK("%s: asoc=%p, timeo=%ld\n", __FUNCTION__, asoc,
+			  (long)(*timeo_p));
+
+	add_wait_queue_exclusive(&asoc->wait, &wait);
+
+	/* Increment the association's refcnt.  */
+	sctp_association_hold(asoc);
+
+	for (;;) {
+		__set_current_state(TASK_INTERRUPTIBLE);
+		if (!*timeo_p)
+			goto do_nonblock;
+		if (sk->shutdown & RCV_SHUTDOWN)
+			break;
+		if (sk->err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING ||
+		    asoc->base.dead)
+			goto do_error;
+		if (signal_pending(current))
+			goto do_interrupted;
+
+		if (asoc->state == SCTP_STATE_ESTABLISHED)
+			break;
+
+		/* Let another process have a go.  Since we are going
+		 * to sleep anyway.
+		 */
+		sctp_release_sock(sk);
+		current_timeo = schedule_timeout(current_timeo);
+		sctp_lock_sock(sk);
+
+		*timeo_p = current_timeo;
+	}
+
+out:
+	remove_wait_queue(&asoc->wait, &wait);
+
+	/* Release the association's refcnt.  */
+	sctp_association_put(asoc);
+
+	__set_current_state(TASK_RUNNING);
+
+	return err;
+
+do_error:
+	err = -ECONNREFUSED;
+	goto out;
+
+do_interrupted:
+	err = sock_intr_errno(*timeo_p);
+	goto out;
+
+do_nonblock:
+	err = -EINPROGRESS;
+	goto out;
+}
+
+static int sctp_wait_for_accept(struct sock *sk, long timeo)
+{
+	struct sctp_endpoint *ep;
+	int err = 0;
+	DECLARE_WAITQUEUE(wait, current);
+
+	ep = sctp_sk(sk)->ep;
+
+	add_wait_queue_exclusive(sk->sleep, &wait);
+
+	for (;;) {
+		__set_current_state(TASK_INTERRUPTIBLE);
+		if (list_empty(&ep->asocs)) {
+			sctp_release_sock(sk);
+			timeo = schedule_timeout(timeo);
+			sctp_lock_sock(sk);
+		}
+
+		err = -EINVAL;
+		if (sk->state != SCTP_SS_LISTENING)
+			break;
+
+		err = 0;
+		if (!list_empty(&ep->asocs))
+			break;
+
+		err = sock_intr_errno(timeo);
+		if (signal_pending(current))
+			break;
+
+		err = -EAGAIN;
+		if (!timeo)
+			break;
+	}
+
+	remove_wait_queue(sk->sleep, &wait);
+	__set_current_state(TASK_RUNNING);
+
+	return err;
+}
+
+/* Populate the fields of the newsk from the oldsk and migrate the assoc
+ * and its messages to the newsk.
+ */
+static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
+			      struct sctp_association *assoc,
+			      sctp_socket_type_t type)
+{
+	struct sctp_opt *oldsp = sctp_sk(oldsk);
+	struct sctp_opt *newsp = sctp_sk(newsk);
+	struct sctp_endpoint *newep = newsp->ep;
+	struct sk_buff *skb, *tmp;
+	struct sctp_ulpevent *event;
+
+	/* Migrate socket buffer sizes and all the socket level options to the
+	 * new socket.
+	 */
+	newsk->sndbuf = oldsk->sndbuf;
+	newsk->rcvbuf = oldsk->rcvbuf;
+	*newsp = *oldsp;
+
+	/* Restore the ep value that was overwritten with the above structure
+	 * copy.
+	 */
+	newsp->ep = newep;
+	newsp->hmac = NULL;
+
+	/* Move any messages in the old socket's receive queue that are for the
+	 * peeled off association to the new socket's receive queue.
+	 */
+	sctp_skb_for_each(skb, &oldsk->receive_queue, tmp) {
+		event = sctp_skb2event(skb);
+		if (event->event_asoc == assoc) {
+			__skb_unlink(skb, skb->list);
+			__skb_queue_tail(&newsk->receive_queue, skb);
+		}
+	}
+
+	/* Clean up any messages pending delivery due to partial
+	 * delivery.   Three cases:
+	 * 1) No partial deliver;  no work.
+	 * 2) Peeling off partial delivery; keep pd_lobby in new pd_lobby.
+	 * 3) Peeling off non-partial delivery; move pd_lobby to recieve_queue.
+	 */
+	skb_queue_head_init(&newsp->pd_lobby);
+	sctp_sk(newsk)->pd_mode = assoc->ulpq.pd_mode;;
+
+	if (sctp_sk(oldsk)->pd_mode) {
+		struct sk_buff_head *queue;
+
+		/* Decide which queue to move pd_lobby skbs to. */
+		if (assoc->ulpq.pd_mode) {
+			queue = &newsp->pd_lobby;
+		} else
+			queue = &newsk->receive_queue;
+
+		/* Walk through the pd_lobby, looking for skbs that
+		 * need moved to the new socket.
+		 */
+		sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) {
+			event = sctp_skb2event(skb);
+			if (event->event_asoc == assoc) {
+				__skb_unlink(skb, skb->list);
+				__skb_queue_tail(queue, skb);
+			}
+		}
+
+		/* Clear up any skbs waiting for the partial
+		 * delivery to finish.
+		 */
+		if (assoc->ulpq.pd_mode)
+			sctp_clear_pd(oldsk);
+
+	}
+
+	/* Set the type of socket to indicate that it is peeled off from the
+	 * original UDP-style socket or created with the accept() call on a
+	 * TCP-style socket..
+	 */
+	newsp->type = type;
+
+	/* Migrate the association to the new socket. */
+	sctp_assoc_migrate(assoc, newsk);
+
+	newsk->state = SCTP_SS_ESTABLISHED;
+}
+
+/* This proto struct describes the ULP interface for SCTP.  */
+struct proto sctp_prot = {
+	.name        =	"SCTP",
+	.close       =	sctp_close,
+	.connect     =	sctp_connect,
+	.disconnect  =	sctp_disconnect,
+	.accept      =	sctp_accept,
+	.ioctl       =	sctp_ioctl,
+	.init        =	sctp_init_sock,
+	.destroy     =	sctp_destroy_sock,
+	.shutdown    =	sctp_shutdown,
+	.setsockopt  =	sctp_setsockopt,
+	.getsockopt  =	sctp_getsockopt,
+	.sendmsg     =	sctp_sendmsg,
+	.recvmsg     =	sctp_recvmsg,
+	.bind        =	sctp_bind,
+	.backlog_rcv =	sctp_backlog_rcv,
+	.hash        =	sctp_hash,
+	.unhash      =	sctp_unhash,
+	.get_port    =	sctp_get_port,
+};
diff -urN linux-2.4.23-pre5/net/sctp/ssnmap.c linux-2.4.23-pre6/net/sctp/ssnmap.c
--- linux-2.4.23-pre5/net/sctp/ssnmap.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/ssnmap.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,113 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 2003 International Business Machines, Corp.
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * These functions manipulate sctp SSN tracker.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    Jon Grimm             <jgrimm@us.ibm.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/types.h>
+#include <net/sctp/sctp.h>
+#include <net/sctp/sm.h>
+
+
+/* Storage size needed for map includes 2 headers and then the
+ * specific needs of in or out streams.
+ */
+static inline size_t sctp_ssnmap_size(__u16 in, __u16 out)
+{
+	return sizeof(struct sctp_ssnmap) + (in + out) * sizeof(__u16);
+}
+
+
+/* Create a new sctp_ssnmap.
+ * Allocate room to store at least 'len' contiguous TSNs.
+ */
+struct sctp_ssnmap *sctp_ssnmap_new(__u16 in, __u16 out, int gfp)
+{
+	struct sctp_ssnmap *retval;
+
+	retval = kmalloc(sctp_ssnmap_size(in, out), gfp);
+
+	if (!retval)
+		goto fail;
+
+	if (!sctp_ssnmap_init(retval, in, out))
+		goto fail_map;
+
+	retval->malloced = 1;
+	SCTP_DBG_OBJCNT_INC(ssnmap);
+
+	return retval;
+
+fail_map:
+	kfree(retval);
+fail:
+	return NULL;
+}
+
+
+/* Initialize a block of memory as a ssnmap.  */
+struct sctp_ssnmap *sctp_ssnmap_init(struct sctp_ssnmap *map, __u16 in,
+				     __u16 out)
+{
+	memset(map, 0x00, sctp_ssnmap_size(in, out));
+
+	/* Start 'in' stream just after the map header. */
+	map->in.ssn = (__u16 *)&map[1];
+	map->in.len = in;
+
+	/* Start 'out' stream just after 'in'. */
+	map->out.ssn = &map->in.ssn[in];
+	map->out.len = out;
+
+	return map;
+}
+
+/* Clear out the ssnmap streams.  */
+void sctp_ssnmap_clear(struct sctp_ssnmap *map)
+{
+	size_t size;
+
+	size = (map->in.len + map->out.len) * sizeof(__u16);
+	memset(map->in.ssn, 0x00, size);
+}
+
+/* Dispose of a ssnmap.  */
+void sctp_ssnmap_free(struct sctp_ssnmap *map)
+{
+	if (map && map->malloced) {
+		kfree(map);
+		SCTP_DBG_OBJCNT_DEC(ssnmap);
+	}
+}
diff -urN linux-2.4.23-pre5/net/sctp/sysctl.c linux-2.4.23-pre6/net/sctp/sysctl.c
--- linux-2.4.23-pre5/net/sctp/sysctl.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/sysctl.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,187 @@
+/* SCTP kernel reference Implementation 
+ * Copyright (c) 2002 International Business Machines Corp.
+ * Copyright (c) 2002 Intel Corp.
+ * 
+ * This file is part of the SCTP kernel reference Implementation
+ * 
+ * Sysctl related interfaces for SCTP. 
+ * 
+ * The SCTP reference implementation is free software; 
+ * you can redistribute it and/or modify it under the terms of 
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ * 
+ * The SCTP reference implementation is distributed in the hope that it 
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.  
+ * 
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ * 
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by: 
+ *    Mingqin Liu           <liuming@us.ibm.com>
+ *    Jon Grimm             <jgrimm@us.ibm.com>
+ *    Ardelle Fan           <ardelle.fan@intel.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <net/sctp/structs.h>
+#include <linux/sysctl.h>
+
+extern struct sctp_protocol sctp_proto;
+
+static ctl_table sctp_table[] = {
+	{
+		.ctl_name	= NET_SCTP_RTO_INITIAL,
+		.procname	= "rto_initial",
+		.data		= &sctp_proto.rto_initial,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_jiffies,
+		.strategy	= &sysctl_jiffies
+	},
+	{
+		.ctl_name	= NET_SCTP_RTO_MIN,
+		.procname	= "rto_min",
+		.data		= &sctp_proto.rto_min,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_jiffies,
+		.strategy	= &sysctl_jiffies
+	},
+	{
+		.ctl_name	= NET_SCTP_RTO_MAX,
+		.procname	= "rto_max",
+		.data		= &sctp_proto.rto_max,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_jiffies,
+		.strategy	= &sysctl_jiffies
+	},
+	{
+		.ctl_name	= NET_SCTP_VALID_COOKIE_LIFE,
+		.procname	= "valid_cookie_life",
+		.data		= &sctp_proto.valid_cookie_life,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_jiffies,
+		.strategy	= &sysctl_jiffies
+	},
+	{
+		.ctl_name	= NET_SCTP_MAX_BURST,
+		.procname	= "max_burst",
+		.data		= &sctp_proto.max_burst,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec
+	},
+	{
+		.ctl_name	= NET_SCTP_ASSOCIATION_MAX_RETRANS,
+		.procname	= "association_max_retrans",
+		.data		= &sctp_proto.max_retrans_association,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec
+	},
+	{
+		.ctl_name	= NET_SCTP_PATH_MAX_RETRANS,
+		.procname	= "path_max_retrans",
+		.data		= &sctp_proto.max_retrans_path,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec
+	},
+	{
+		.ctl_name	= NET_SCTP_MAX_INIT_RETRANSMITS,
+		.procname	= "max_init_retransmits",
+		.data		= &sctp_proto.max_retrans_init,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec
+	},
+	{
+		.ctl_name	= NET_SCTP_HB_INTERVAL,
+		.procname	= "hb_interval",
+		.data		= &sctp_proto.hb_interval,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_jiffies,
+		.strategy	= &sysctl_jiffies
+	},
+	{
+		.ctl_name	= NET_SCTP_PRESERVE_ENABLE,
+		.procname	= "cookie_preserve_enable",
+		.data		= &sctp_proto.cookie_preserve_enable,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_jiffies,
+		.strategy	= &sysctl_jiffies
+	},
+	{
+		.ctl_name	= NET_SCTP_RTO_ALPHA,
+		.procname	= "rto_alpha_exp_divisor",
+		.data		= &sctp_proto.rto_alpha,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec
+	},
+	{
+		.ctl_name	= NET_SCTP_RTO_BETA,
+		.procname	= "rto_beta_exp_divisor",
+		.data		= &sctp_proto.rto_beta,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec
+	},
+	{ .ctl_name = 0 }
+};
+
+static ctl_table sctp_net_table[] = {
+	{
+		.ctl_name	= NET_SCTP,
+		.procname	= "sctp",
+		.maxlen		= 0,
+		.mode		= 0555,
+		.child		= sctp_table
+	},
+	{ .ctl_name = 0 }
+};
+
+static ctl_table sctp_root_table[] = {
+	{
+		.ctl_name	= CTL_NET,
+		.procname	= "net",
+		.maxlen		= 0,
+		.mode		= 0555,
+		.child		= sctp_net_table
+	},
+	{ .ctl_name = 0 }
+};
+
+static struct ctl_table_header * sctp_sysctl_header;
+
+/* Sysctl registration.  */
+void sctp_sysctl_register(void)
+{
+	sctp_sysctl_header = register_sysctl_table(sctp_root_table, 0);
+}
+
+/* Sysctl deregistration.  */
+void sctp_sysctl_unregister(void)
+{
+	unregister_sysctl_table(sctp_sysctl_header);
+}
diff -urN linux-2.4.23-pre5/net/sctp/transport.c linux-2.4.23-pre6/net/sctp/transport.c
--- linux-2.4.23-pre5/net/sctp/transport.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/transport.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,500 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001-2003 International Business Machines Corp.
+ * Copyright (c) 2001 Intel Corp.
+ * Copyright (c) 2001 La Monte H.P. Yarroll
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * This module provides the abstraction for an SCTP tranport representing
+ * a remote transport address.  For local transport addresses, we just use
+ * union sctp_addr.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Karl Knutson          <karl@athena.chicago.il.us>
+ *    Jon Grimm             <jgrimm@us.ibm.com>
+ *    Xingang Guo           <xingang.guo@intel.com>
+ *    Hui Huang             <hui.huang@nokia.com>
+ *    Sridhar Samudrala	    <sri@us.ibm.com>
+ *    Ardelle Fan	    <ardelle.fan@intel.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/types.h>
+#include <net/sctp/sctp.h>
+
+/* 1st Level Abstractions.  */
+
+/* Allocate and initialize a new transport.  */
+struct sctp_transport *sctp_transport_new(const union sctp_addr *addr, int gfp)
+{
+        struct sctp_transport *transport;
+
+        transport = t_new(struct sctp_transport, gfp);
+	if (!transport)
+		goto fail;
+
+	if (!sctp_transport_init(transport, addr, gfp))
+		goto fail_init;
+
+	transport->malloced = 1;
+	SCTP_DBG_OBJCNT_INC(transport);
+
+	return transport;
+
+fail_init:
+	kfree(transport);
+
+fail:
+	return NULL;
+}
+
+/* Initialize a new transport from provided memory.  */
+struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
+					   const union sctp_addr *addr,
+					   int gfp)
+{
+	struct sctp_protocol *proto = sctp_get_protocol();
+
+	/* Copy in the address.  */
+	peer->ipaddr = *addr;
+	peer->af_specific = sctp_get_af_specific(addr->sa.sa_family);
+	peer->asoc = NULL;
+
+	peer->dst = NULL;
+	memset(&peer->saddr, 0, sizeof(union sctp_addr));
+
+	/* From 6.3.1 RTO Calculation:
+	 *
+	 * C1) Until an RTT measurement has been made for a packet sent to the
+	 * given destination transport address, set RTO to the protocol
+	 * parameter 'RTO.Initial'.
+	 */
+	peer->rtt = 0;
+	peer->rto = proto->rto_initial;
+	peer->rttvar = 0;
+	peer->srtt = 0;
+	peer->rto_pending = 0;
+
+	peer->last_time_heard = jiffies;
+	peer->last_time_used = jiffies;
+	peer->last_time_ecne_reduced = jiffies;
+
+	peer->active = 1;
+	peer->hb_allowed = 0;
+
+	/* Initialize the default path max_retrans.  */
+	peer->max_retrans = proto->max_retrans_path;
+	peer->error_threshold = 0;
+	peer->error_count = 0;
+
+	peer->debug_name = "unnamedtransport";
+
+	INIT_LIST_HEAD(&peer->transmitted);
+	INIT_LIST_HEAD(&peer->send_ready);
+	INIT_LIST_HEAD(&peer->transports);
+
+	/* Set up the retransmission timer.  */
+	init_timer(&peer->T3_rtx_timer);
+	peer->T3_rtx_timer.function = sctp_generate_t3_rtx_event;
+	peer->T3_rtx_timer.data = (unsigned long)peer;
+
+	/* Set up the heartbeat timer. */
+	init_timer(&peer->hb_timer);
+	peer->hb_interval = SCTP_DEFAULT_TIMEOUT_HEARTBEAT;
+	peer->hb_timer.function = sctp_generate_heartbeat_event;
+	peer->hb_timer.data = (unsigned long)peer;
+
+	atomic_set(&peer->refcnt, 1);
+	peer->dead = 0;
+
+	peer->malloced = 0;
+	return peer;
+}
+
+/* This transport is no longer needed.  Free up if possible, or
+ * delay until it last reference count.
+ */
+void sctp_transport_free(struct sctp_transport *transport)
+{
+	transport->dead = 1;
+
+	/* Try to delete the heartbeat timer.  */
+	if (del_timer(&transport->hb_timer))
+		sctp_transport_put(transport);
+
+	sctp_transport_put(transport);
+}
+
+/* Destroy the transport data structure.
+ * Assumes there are no more users of this structure.
+ */
+void sctp_transport_destroy(struct sctp_transport *transport)
+{
+	SCTP_ASSERT(transport->dead, "Transport is not dead", return);
+
+	if (transport->asoc)
+		sctp_association_put(transport->asoc);
+
+	dst_release(transport->dst);
+	kfree(transport);
+	SCTP_DBG_OBJCNT_DEC(transport);
+}
+
+/* Start T3_rtx timer if it is not already running and update the heartbeat
+ * timer.  This routine is called every time a DATA chunk is sent.
+ */
+void sctp_transport_reset_timers(struct sctp_transport *transport)
+{
+	/* RFC 2960 6.3.2 Retransmission Timer Rules
+	 *
+	 * R1) Every time a DATA chunk is sent to any address(including a
+	 * retransmission), if the T3-rtx timer of that address is not running
+	 * start it running so that it will expire after the RTO of that
+	 * address.
+	 */
+
+	if (!timer_pending(&transport->T3_rtx_timer))
+		if (!mod_timer(&transport->T3_rtx_timer,
+			       jiffies + transport->rto))
+			sctp_transport_hold(transport);
+
+	/* When a data chunk is sent, reset the heartbeat interval.  */
+	if (!mod_timer(&transport->hb_timer,
+		       sctp_transport_timeout(transport)))
+	    sctp_transport_hold(transport);
+}
+
+/* This transport has been assigned to an association.
+ * Initialize fields from the association or from the sock itself.
+ * Register the reference count in the association.
+ */
+void sctp_transport_set_owner(struct sctp_transport *transport,
+			      struct sctp_association *asoc)
+{
+	transport->asoc = asoc;
+	sctp_association_hold(asoc);
+}
+
+/* Initialize the pmtu of a transport. */
+void sctp_transport_pmtu(struct sctp_transport *transport)
+{
+	struct dst_entry *dst;
+
+	dst = transport->af_specific->get_dst(NULL, &transport->ipaddr, NULL);
+
+	if (dst) {
+		transport->pmtu = dst_pmtu(dst);
+		dst_release(dst);
+	} else
+		transport->pmtu = SCTP_DEFAULT_MAXSEGMENT;
+}
+
+/* Caches the dst entry and source address for a transport's destination
+ * address.
+ */
+void sctp_transport_route(struct sctp_transport *transport,
+			  union sctp_addr *saddr, struct sctp_opt *opt)
+{
+	struct sctp_association *asoc = transport->asoc;
+	struct sctp_af *af = transport->af_specific;
+	union sctp_addr *daddr = &transport->ipaddr;
+	struct dst_entry *dst;
+
+	dst = af->get_dst(asoc, daddr, saddr);
+
+	if (saddr)
+		memcpy(&transport->saddr, saddr, sizeof(union sctp_addr));
+	else
+		af->get_saddr(asoc, dst, daddr, &transport->saddr);
+
+	transport->dst = dst;
+	if (dst) {
+		transport->pmtu = dst_pmtu(dst);
+
+		/* Initialize sk->rcv_saddr, if the transport is the
+		 * association's active path for getsockname().
+		 */ 
+		if (asoc && (transport == asoc->peer.active_path))
+			af->to_sk_saddr(&transport->saddr, asoc->base.sk);
+	} else
+		transport->pmtu = SCTP_DEFAULT_MAXSEGMENT;
+}
+
+/* Hold a reference to a transport.  */
+void sctp_transport_hold(struct sctp_transport *transport)
+{
+	atomic_inc(&transport->refcnt);
+}
+
+/* Release a reference to a transport and clean up
+ * if there are no more references.
+ */
+void sctp_transport_put(struct sctp_transport *transport)
+{
+	if (atomic_dec_and_test(&transport->refcnt))
+		sctp_transport_destroy(transport);
+}
+
+/* Update transport's RTO based on the newly calculated RTT. */
+void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt)
+{
+	struct sctp_protocol *proto = sctp_get_protocol();
+
+	/* Check for valid transport.  */
+	SCTP_ASSERT(tp, "NULL transport", return);
+
+	/* We should not be doing any RTO updates unless rto_pending is set.  */
+	SCTP_ASSERT(tp->rto_pending, "rto_pending not set", return);
+
+	if (tp->rttvar || tp->srtt) {
+		/* 6.3.1 C3) When a new RTT measurement R' is made, set
+		 * RTTVAR <- (1 - RTO.Beta) * RTTVAR + RTO.Beta * |SRTT - R'|
+		 * SRTT <- (1 - RTO.Alpha) * SRTT + RTO.Alpha * R'
+		 */
+
+		/* Note:  The above algorithm has been rewritten to
+		 * express rto_beta and rto_alpha as inverse powers
+		 * of two.
+		 * For example, assuming the default value of RTO.Alpha of
+		 * 1/8, rto_alpha would be expressed as 3.
+		 */
+		tp->rttvar = tp->rttvar - (tp->rttvar >> proto->rto_beta)
+			+ ((abs(tp->srtt - rtt)) >> proto->rto_beta);
+		tp->srtt = tp->srtt - (tp->srtt >> proto->rto_alpha)
+			+ (rtt >> proto->rto_alpha);
+	} else {
+		/* 6.3.1 C2) When the first RTT measurement R is made, set
+		 * SRTT <- R, RTTVAR <- R/2.
+		 */
+		tp->srtt = rtt;
+		tp->rttvar = rtt >> 1;
+	}
+
+	/* 6.3.1 G1) Whenever RTTVAR is computed, if RTTVAR = 0, then
+	 * adjust RTTVAR <- G, where G is the CLOCK GRANULARITY.
+	 */
+	if (tp->rttvar == 0)
+		tp->rttvar = SCTP_CLOCK_GRANULARITY;
+
+	/* 6.3.1 C3) After the computation, update RTO <- SRTT + 4 * RTTVAR. */
+	tp->rto = tp->srtt + (tp->rttvar << 2);
+
+	/* 6.3.1 C6) Whenever RTO is computed, if it is less than RTO.Min
+	 * seconds then it is rounded up to RTO.Min seconds.
+	 */
+	if (tp->rto < tp->asoc->rto_min)
+		tp->rto = tp->asoc->rto_min;
+
+	/* 6.3.1 C7) A maximum value may be placed on RTO provided it is
+	 * at least RTO.max seconds.
+	 */
+	if (tp->rto > tp->asoc->rto_max)
+		tp->rto = tp->asoc->rto_max;
+
+	tp->rtt = rtt;
+
+	/* Reset rto_pending so that a new RTT measurement is started when a
+	 * new data chunk is sent.
+	 */
+	tp->rto_pending = 0;
+
+	SCTP_DEBUG_PRINTK("%s: transport: %p, rtt: %d, srtt: %d "
+			  "rttvar: %d, rto: %d\n", __FUNCTION__,
+			  tp, rtt, tp->srtt, tp->rttvar, tp->rto);
+}
+
+/* This routine updates the transport's cwnd and partial_bytes_acked
+ * parameters based on the bytes acked in the received SACK.
+ */
+void sctp_transport_raise_cwnd(struct sctp_transport *transport,
+			       __u32 sack_ctsn, __u32 bytes_acked)
+{
+	__u32 cwnd, ssthresh, flight_size, pba, pmtu;
+
+	cwnd = transport->cwnd;
+	flight_size = transport->flight_size;
+
+	/* The appropriate cwnd increase algorithm is performed if, and only
+	 * if the cumulative TSN has advanced and the congestion window is
+	 * being fully utilized.
+	 */
+	if ((transport->asoc->ctsn_ack_point >= sack_ctsn) ||
+	    (flight_size < cwnd))
+		return;
+
+	ssthresh = transport->ssthresh;
+	pba = transport->partial_bytes_acked;
+	pmtu = transport->asoc->pmtu;
+
+	if (cwnd <= ssthresh) {
+		/* RFC 2960 7.2.1, sctpimpguide-05 2.14.2 When cwnd is less
+		 * than or equal to ssthresh an SCTP endpoint MUST use the
+		 * slow start algorithm to increase cwnd only if the current
+		 * congestion window is being fully utilized and an incoming
+		 * SACK advances the Cumulative TSN Ack Point. Only when these
+		 * two conditions are met can the cwnd be increased otherwise
+		 * the cwnd MUST not be increased. If these conditions are met
+		 * then cwnd MUST be increased by at most the lesser of
+		 * 1) the total size of the previously outstanding DATA
+		 * chunk(s) acknowledged, and 2) the destination's path MTU.
+		 */
+		if (bytes_acked > pmtu)
+			cwnd += pmtu;
+		else
+			cwnd += bytes_acked;
+		SCTP_DEBUG_PRINTK("%s: SLOW START: transport: %p, "
+				  "bytes_acked: %d, cwnd: %d, ssthresh: %d, "
+				  "flight_size: %d, pba: %d\n",
+				  __FUNCTION__,
+				  transport, bytes_acked, cwnd,
+				  ssthresh, flight_size, pba);
+	} else {
+		/* RFC 2960 7.2.2 Whenever cwnd is greater than ssthresh,
+		 * upon each SACK arrival that advances the Cumulative TSN Ack
+		 * Point, increase partial_bytes_acked by the total number of
+		 * bytes of all new chunks acknowledged in that SACK including
+		 * chunks acknowledged by the new Cumulative TSN Ack and by
+		 * Gap Ack Blocks.
+		 *
+		 * When partial_bytes_acked is equal to or greater than cwnd
+		 * and before the arrival of the SACK the sender had cwnd or
+		 * more bytes of data outstanding (i.e., before arrival of the
+		 * SACK, flightsize was greater than or equal to cwnd),
+		 * increase cwnd by MTU, and reset partial_bytes_acked to
+		 * (partial_bytes_acked - cwnd).
+		 */
+		pba += bytes_acked;
+		if (pba >= cwnd) {
+			cwnd += pmtu;
+			pba = ((cwnd < pba) ? (pba - cwnd) : 0);
+		}
+		SCTP_DEBUG_PRINTK("%s: CONGESTION AVOIDANCE: "
+				  "transport: %p, bytes_acked: %d, cwnd: %d, "
+				  "ssthresh: %d, flight_size: %d, pba: %d\n",
+				  __FUNCTION__,
+				  transport, bytes_acked, cwnd,
+				  ssthresh, flight_size, pba);
+	}
+
+	transport->cwnd = cwnd;
+	transport->partial_bytes_acked = pba;
+}
+
+/* This routine is used to lower the transport's cwnd when congestion is
+ * detected.
+ */
+void sctp_transport_lower_cwnd(struct sctp_transport *transport,
+			       sctp_lower_cwnd_t reason)
+{
+	switch (reason) {
+	case SCTP_LOWER_CWND_T3_RTX:
+		/* RFC 2960 Section 7.2.3, sctpimpguide-05 Section 2.9.2
+		 * When the T3-rtx timer expires on an address, SCTP should
+		 * perform slow start by:
+		 *      ssthresh = max(cwnd/2, 2*MTU)
+		 *      cwnd = 1*MTU
+		 *      partial_bytes_acked = 0
+		 */
+		transport->ssthresh = max(transport->cwnd/2,
+					  2*transport->asoc->pmtu);
+		transport->cwnd = transport->asoc->pmtu;
+		break;
+
+	case SCTP_LOWER_CWND_FAST_RTX:
+		/* RFC 2960 7.2.4 Adjust the ssthresh and cwnd of the
+		 * destination address(es) to which the missing DATA chunks
+		 * were last sent, according to the formula described in
+		 * Section 7.2.3.
+	 	 *
+	 	 * RFC 2960 7.2.3, sctpimpguide-05 2.9.2 Upon detection of
+		 * packet losses from SACK (see Section 7.2.4), An endpoint
+		 * should do the following:
+		 *      ssthresh = max(cwnd/2, 2*MTU)
+		 *      cwnd = ssthresh
+		 *      partial_bytes_acked = 0
+		 */
+		transport->ssthresh = max(transport->cwnd/2,
+					  2*transport->asoc->pmtu);
+		transport->cwnd = transport->ssthresh;
+		break;
+
+	case SCTP_LOWER_CWND_ECNE:
+		/* RFC 2481 Section 6.1.2.
+		 * If the sender receives an ECN-Echo ACK packet
+		 * then the sender knows that congestion was encountered in the
+		 * network on the path from the sender to the receiver. The
+		 * indication of congestion should be treated just as a
+		 * congestion loss in non-ECN Capable TCP. That is, the TCP
+		 * source halves the congestion window "cwnd" and reduces the
+		 * slow start threshold "ssthresh".
+		 * A critical condition is that TCP does not react to
+		 * congestion indications more than once every window of
+		 * data (or more loosely more than once every round-trip time).
+		 */
+		if ((jiffies - transport->last_time_ecne_reduced) >
+		    transport->rtt) {
+			transport->ssthresh = max(transport->cwnd/2,
+					  	  2*transport->asoc->pmtu);
+			transport->cwnd = transport->ssthresh;
+			transport->last_time_ecne_reduced = jiffies;
+		}
+		break;
+
+	case SCTP_LOWER_CWND_INACTIVE:
+		/* RFC 2960 Section 7.2.1, sctpimpguide-05 Section 2.14.2
+		 * When the association does not transmit data on a given
+		 * transport address within an RTO, the cwnd of the transport
+		 * address should be adjusted to 2*MTU.
+		 * NOTE: Although the draft recommends that this check needs
+		 * to be done every RTO interval, we do it every hearbeat
+		 * interval.
+		 */
+		if ((jiffies - transport->last_time_used) > transport->rto)
+			transport->cwnd = 2*transport->asoc->pmtu;
+		break;
+	};
+
+	transport->partial_bytes_acked = 0;
+	SCTP_DEBUG_PRINTK("%s: transport: %p reason: %d cwnd: "
+			  "%d ssthresh: %d\n", __FUNCTION__,
+			  transport, reason,
+			  transport->cwnd, transport->ssthresh);
+}
+
+/* What is the next timeout value for this transport? */
+unsigned long sctp_transport_timeout(struct sctp_transport *t)
+{
+	unsigned long timeout;
+	timeout = t->hb_interval + t->rto + sctp_jitter(t->rto);
+	timeout += jiffies;
+	return timeout;
+}
diff -urN linux-2.4.23-pre5/net/sctp/tsnmap.c linux-2.4.23-pre6/net/sctp/tsnmap.c
--- linux-2.4.23-pre5/net/sctp/tsnmap.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/tsnmap.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,406 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001 International Business Machines, Corp.
+ * Copyright (c) 2001 Intel Corp.
+ *
+ * This file is part of the SCTP kernel reference Implementation
+ *
+ * These functions manipulate sctp tsn mapping array.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Jon Grimm             <jgrimm@us.ibm.com>
+ *    Karl Knutson          <karl@athena.chicago.il.us>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/types.h>
+#include <net/sctp/sctp.h>
+#include <net/sctp/sm.h>
+
+static void sctp_tsnmap_update(struct sctp_tsnmap *map);
+static void sctp_tsnmap_update_pending_data(struct sctp_tsnmap *map);
+static void sctp_tsnmap_find_gap_ack(__u8 *map, __u16 off,
+				     __u16 len, __u16 base,
+				     int *started, __u16 *start,
+				     int *ended, __u16 *end);
+
+/* Create a new sctp_tsnmap.
+ * Allocate room to store at least 'len' contiguous TSNs.
+ */
+struct sctp_tsnmap *sctp_tsnmap_new(__u16 len, __u32 initial_tsn, int gfp)
+{
+	struct sctp_tsnmap *retval;
+
+	retval = kmalloc(sizeof(struct sctp_tsnmap) +
+			 sctp_tsnmap_storage_size(len), gfp);
+	if (!retval)
+		goto fail;
+
+	if (!sctp_tsnmap_init(retval, len, initial_tsn))
+		goto fail_map;
+	retval->malloced = 1;
+	return retval;
+
+fail_map:
+	kfree(retval);
+fail:
+	return NULL;
+}
+
+/* Initialize a block of memory as a tsnmap.  */
+struct sctp_tsnmap *sctp_tsnmap_init(struct sctp_tsnmap *map, __u16 len,
+				     __u32 initial_tsn)
+{
+	map->tsn_map = map->raw_map;
+	map->overflow_map = map->tsn_map + len;
+	map->len = len;
+
+	/* Clear out a TSN ack status.  */
+	memset(map->tsn_map, 0x00, map->len + map->len);
+
+	/* Keep track of TSNs represented by tsn_map.  */
+	map->base_tsn = initial_tsn;
+	map->overflow_tsn = initial_tsn + map->len;
+	map->cumulative_tsn_ack_point = initial_tsn - 1;
+	map->max_tsn_seen = map->cumulative_tsn_ack_point;
+	map->malloced = 0;
+	map->pending_data = 0;
+	map->num_dup_tsns = 0;
+
+	return map;
+}
+
+/* Test the tracking state of this TSN.
+ * Returns:
+ *   0 if the TSN has not yet been seen
+ *  >0 if the TSN has been seen (duplicate)
+ *  <0 if the TSN is invalid (too large to track)
+ */
+int sctp_tsnmap_check(const struct sctp_tsnmap *map, __u32 tsn)
+{
+	__s32 gap;
+	int dup;
+
+	/* Calculate the index into the mapping arrays.  */
+	gap = tsn - map->base_tsn;
+
+	/* Verify that we can hold this TSN.  */
+	if (gap >= (/* base */ map->len + /* overflow */ map->len)) {
+		dup = -1;
+		goto out;
+	}
+
+	/* Honk if we've already seen this TSN.
+	 * We have three cases:
+	 *	1. The TSN is ancient or belongs to a previous tsn_map.
+	 *	2. The TSN is already marked in the tsn_map.
+	 *	3. The TSN is already marked in the tsn_map_overflow.
+	 */
+	if (gap < 0 ||
+	    (gap < map->len && map->tsn_map[gap]) ||
+	    (gap >= map->len && map->overflow_map[gap - map->len]))
+		dup = 1;
+	else
+		dup = 0;
+
+out:
+	return dup;
+}
+
+/* Is there a gap in the TSN map?  */
+int sctp_tsnmap_has_gap(const struct sctp_tsnmap *map)
+{
+	int has_gap;
+
+	has_gap = (map->cumulative_tsn_ack_point != map->max_tsn_seen);
+	return has_gap;
+}
+
+/* Mark this TSN as seen.  */
+void sctp_tsnmap_mark(struct sctp_tsnmap *map, __u32 tsn)
+{
+	__s32 gap;
+
+	/* Vacuously mark any TSN which precedes the map base or
+	 * exceeds the end of the map.
+	 */
+	if (TSN_lt(tsn, map->base_tsn))
+		return;
+	if (!TSN_lt(tsn, map->base_tsn + map->len + map->len))
+		return;
+
+	/* Bump the max.  */
+	if (TSN_lt(map->max_tsn_seen, tsn))
+		map->max_tsn_seen = tsn;
+
+	/* Assert: TSN is in range.  */
+	gap = tsn - map->base_tsn;
+
+	/* Mark the TSN as received.  */
+	if (gap < map->len)
+		map->tsn_map[gap]++;
+	else
+		map->overflow_map[gap - map->len]++;
+
+	/* Go fixup any internal TSN mapping variables including
+	 * cumulative_tsn_ack_point.
+	 */
+	sctp_tsnmap_update(map);
+}
+
+void sctp_tsnmap_report_dup(struct sctp_tsnmap *map, __u32 tsn)
+{
+}
+
+/* Retrieve the Cumulative TSN Ack Point. */
+__u32 sctp_tsnmap_get_ctsn(const struct sctp_tsnmap *map)
+{
+	return map->cumulative_tsn_ack_point;
+}
+
+/* Retrieve the highest TSN we've seen.  */
+__u32 sctp_tsnmap_get_max_tsn_seen(const struct sctp_tsnmap *map)
+{
+	return map->max_tsn_seen;
+}
+
+/* Dispose of a tsnmap.  */
+void sctp_tsnmap_free(struct sctp_tsnmap *map)
+{
+	if (map->malloced)
+		kfree(map);
+}
+
+/* Initialize a Gap Ack Block iterator from memory being provided.  */
+void sctp_tsnmap_iter_init(const struct sctp_tsnmap *map,
+			   struct sctp_tsnmap_iter *iter)
+{
+	/* Only start looking one past the Cumulative TSN Ack Point.  */
+	iter->start = map->cumulative_tsn_ack_point + 1;
+}
+
+/* Get the next Gap Ack Blocks. Returns 0 if there was not another block
+ * to get.
+ */
+int sctp_tsnmap_next_gap_ack(const struct sctp_tsnmap *map,
+	struct sctp_tsnmap_iter *iter, __u16 *start, __u16 *end)
+{
+	int started, ended;
+	__u16 _start, _end, offset;
+
+	/* We haven't found a gap yet.  */
+	started = ended = 0;
+
+	/* Search the first mapping array.  */
+	if (iter->start - map->base_tsn < map->len) {
+
+		offset = iter->start - map->base_tsn;
+		sctp_tsnmap_find_gap_ack(map->tsn_map, offset, map->len, 0,
+					 &started, &_start, &ended, &_end);
+	}
+
+	/* Do we need to check the overflow map? */
+	if (!ended) {
+		/* Fix up where we'd like to start searching in the
+		 * overflow map.
+		 */
+		if (iter->start - map->base_tsn < map->len)
+			offset = 0;
+		else
+			offset = iter->start - map->base_tsn - map->len;
+
+		/* Search the overflow map.  */
+		sctp_tsnmap_find_gap_ack(map->overflow_map,
+					 offset,
+					 map->len,
+					 map->len,
+					 &started, &_start,
+					 &ended, &_end);
+	}
+
+	/* The Gap Ack Block happens to end at the end of the
+	 * overflow map.
+	 */
+	if (started && !ended) {
+		ended++;
+		_end = map->len + map->len - 1;
+	}
+
+	/* If we found a Gap Ack Block, return the start and end and
+	 * bump the iterator forward.
+	 */
+	if (ended) {
+		/* Fix up the start and end based on the
+		 * Cumulative TSN Ack offset into the map.
+		 */
+		int gap = map->cumulative_tsn_ack_point -
+			map->base_tsn;
+
+		*start = _start - gap;
+		*end = _end - gap;
+
+		/* Move the iterator forward.  */
+		iter->start = map->cumulative_tsn_ack_point + *end + 1;
+	}
+
+	return ended;
+}
+
+/********************************************************************
+ * 2nd Level Abstractions
+ ********************************************************************/
+
+/* This private helper function updates the tsnmap buffers and
+ * the Cumulative TSN Ack Point.
+ */
+static void sctp_tsnmap_update(struct sctp_tsnmap *map)
+{
+	__u32 ctsn;
+
+	ctsn = map->cumulative_tsn_ack_point;
+	do {
+		ctsn++;
+		if (ctsn == map->overflow_tsn) {
+			/* Now tsn_map must have been all '1's,
+			 * so we swap the map and check the overflow table
+			 */
+        		__u8 *tmp = map->tsn_map;
+			memset(tmp, 0, map->len);
+			map->tsn_map = map->overflow_map;
+			map->overflow_map = tmp;
+
+			/* Update the tsn_map boundaries.  */
+			map->base_tsn += map->len;
+			map->overflow_tsn += map->len;
+		}
+	} while (map->tsn_map[ctsn - map->base_tsn]);
+
+	map->cumulative_tsn_ack_point = ctsn - 1; /* Back up one. */
+	sctp_tsnmap_update_pending_data(map);
+}
+
+static void sctp_tsnmap_update_pending_data(struct sctp_tsnmap *map)
+{
+	__u32 cum_tsn = map->cumulative_tsn_ack_point;
+	__u32 max_tsn = map->max_tsn_seen;
+	__u32 base_tsn = map->base_tsn;
+	__u16 pending_data;
+	__s32 gap, start, end, i;
+
+	pending_data = max_tsn - cum_tsn;
+	gap = max_tsn - base_tsn;
+
+	if (gap <= 0 || gap >= (map->len + map->len))
+		goto out;
+
+	start = ((cum_tsn >= base_tsn) ? (cum_tsn - base_tsn + 1) : 0);
+	end = ((gap > map->len ) ? map->len : gap + 1);
+
+	for (i = start; i < end; i++) {
+		if (map->tsn_map[i])
+			pending_data--;
+	}
+
+	if (gap >= map->len) {
+		start = 0;
+		end = gap - map->len + 1;
+		for (i = start; i < end; i++) {
+			if (map->overflow_map[i])
+				pending_data--;
+		}
+	}
+
+out:
+	map->pending_data = pending_data;
+}
+
+/* This is a private helper for finding Gap Ack Blocks.  It searches a
+ * single array for the start and end of a Gap Ack Block.
+ *
+ * The flags "started" and "ended" tell is if we found the beginning
+ * or (respectively) the end of a Gap Ack Block.
+ */
+static void sctp_tsnmap_find_gap_ack(__u8 *map, __u16 off,
+				     __u16 len, __u16 base,
+				     int *started, __u16 *start,
+				     int *ended, __u16 *end)
+{
+	int i = off;
+
+	/* Look through the entire array, but break out
+	 * early if we have found the end of the Gap Ack Block.
+	 */
+
+	/* Look for the start. */
+	if (!(*started)) {
+		for (; i < len; i++) {
+			if (map[i]) {
+				(*started)++;
+				*start = base + i;
+				break;
+			}
+		}
+	}
+
+	/* Look for the end.  */
+	if (*started) {
+		/* We have found the start, let's find the
+		 * end.  If we find the end, break out.
+		 */
+		for (; i < len; i++) {
+			if (!map[i]) {
+				(*ended)++;
+				*end = base + i - 1;
+				break;
+			}
+		}
+	}
+}
+
+/* Renege that we have seen a TSN.  */
+void sctp_tsnmap_renege(struct sctp_tsnmap *map, __u32 tsn)
+{
+	__s32 gap;
+
+	if (TSN_lt(tsn, map->base_tsn))
+		return;
+	if (!TSN_lt(tsn, map->base_tsn + map->len + map->len))
+		return;
+
+	/* Assert: TSN is in range.  */
+	gap = tsn - map->base_tsn;
+
+	/* Pretend we never saw the TSN.  */
+	if (gap < map->len)
+		map->tsn_map[gap] = 0;
+	else
+		map->overflow_map[gap - map->len] = 0;
+}
diff -urN linux-2.4.23-pre5/net/sctp/ulpevent.c linux-2.4.23-pre6/net/sctp/ulpevent.c
--- linux-2.4.23-pre5/net/sctp/ulpevent.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/ulpevent.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,883 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001 International Business Machines, Corp.
+ * Copyright (c) 2001 Intel Corp.
+ * Copyright (c) 2001 Nokia, Inc.
+ * Copyright (c) 2001 La Monte H.P. Yarroll
+ *
+ * These functions manipulate an sctp event.   The struct ulpevent is used
+ * to carry notifications and data to the ULP (sockets).
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    Jon Grimm             <jgrimm@us.ibm.com>
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <net/sctp/structs.h>
+#include <net/sctp/sctp.h>
+#include <net/sctp/sm.h>
+
+static void sctp_ulpevent_set_owner_r(struct sk_buff *skb,
+				      struct sctp_association *asoc);
+static void sctp_ulpevent_set_owner(struct sk_buff *skb,
+				    const struct sctp_association *asoc);
+
+/* Create a new sctp_ulpevent.  */
+struct sctp_ulpevent *sctp_ulpevent_new(int size, int msg_flags, int gfp)
+{
+	struct sctp_ulpevent *event;
+	struct sk_buff *skb;
+
+	skb = alloc_skb(size, gfp);
+	if (!skb)
+		goto fail;
+
+	event = sctp_skb2event(skb);
+	event = sctp_ulpevent_init(event, msg_flags);
+	if (!event)
+		goto fail_init;
+	return event;
+
+fail_init:
+	kfree_skb(skb);
+fail:
+	return NULL;
+}
+
+/* Initialize an ULP event from an given skb.  */
+struct sctp_ulpevent *sctp_ulpevent_init(struct sctp_ulpevent *event,
+					 int msg_flags)
+{
+	memset(event, sizeof(struct sctp_ulpevent), 0x00);
+	event->msg_flags = msg_flags;
+	return event;
+}
+
+/* Dispose of an event.  */
+void sctp_ulpevent_free(struct sctp_ulpevent *event)
+{
+	kfree_skb(sctp_event2skb(event));
+}
+
+/* Is this a MSG_NOTIFICATION?  */
+int sctp_ulpevent_is_notification(const struct sctp_ulpevent *event)
+{
+	return MSG_NOTIFICATION == (event->msg_flags & MSG_NOTIFICATION);
+}
+
+/* Create and initialize an SCTP_ASSOC_CHANGE event.
+ *
+ * 5.3.1.1 SCTP_ASSOC_CHANGE
+ *
+ * Communication notifications inform the ULP that an SCTP association
+ * has either begun or ended. The identifier for a new association is
+ * provided by this notification.
+ *
+ * Note: There is no field checking here.  If a field is unused it will be
+ * zero'd out.
+ */
+struct sctp_ulpevent  *sctp_ulpevent_make_assoc_change(
+	const struct sctp_association *asoc,
+	__u16 flags, __u16 state, __u16 error, __u16 outbound,
+	__u16 inbound, int gfp)
+{
+	struct sctp_ulpevent *event;
+	struct sctp_assoc_change *sac;
+	struct sk_buff *skb;
+
+	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));
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.1 SCTP_ASSOC_CHANGE
+	 *
+	 * sac_type:
+	 * It should be SCTP_ASSOC_CHANGE.
+	 */
+	sac->sac_type = SCTP_ASSOC_CHANGE;
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.1 SCTP_ASSOC_CHANGE
+	 *
+	 * sac_state: 32 bits (signed integer)
+	 * This field holds one of a number of values that communicate the
+	 * event that happened to the association.
+	 */
+	sac->sac_state = state;
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.1 SCTP_ASSOC_CHANGE
+	 *
+	 * sac_flags: 16 bits (unsigned integer)
+	 * Currently unused.
+	 */
+	sac->sac_flags = 0;
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.1 SCTP_ASSOC_CHANGE
+	 *
+	 * sac_length: sizeof (__u32)
+	 * This field is the total length of the notification data, including
+	 * the notification header.
+	 */
+	sac->sac_length = sizeof(struct sctp_assoc_change);
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.1 SCTP_ASSOC_CHANGE
+	 *
+	 * sac_error:  32 bits (signed integer)
+	 *
+	 * If the state was reached due to a error condition (e.g.
+	 * COMMUNICATION_LOST) any relevant error information is available in
+	 * this field. This corresponds to the protocol error codes defined in
+	 * [SCTP].
+	 */
+	sac->sac_error = error;
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.1 SCTP_ASSOC_CHANGE
+	 *
+	 * sac_outbound_streams:  16 bits (unsigned integer)
+	 * sac_inbound_streams:  16 bits (unsigned integer)
+	 *
+	 * The maximum number of streams allowed in each direction are
+	 * available in sac_outbound_streams and sac_inbound streams.
+	 */
+	sac->sac_outbound_streams = outbound;
+	sac->sac_inbound_streams = inbound;
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.1 SCTP_ASSOC_CHANGE
+	 *
+	 * sac_assoc_id: sizeof (sctp_assoc_t)
+	 *
+	 * The association id field, holds the identifier for the association.
+	 * All notifications for a given association have the same association
+	 * identifier.  For TCP style socket, this field is ignored.
+	 */
+	sctp_ulpevent_set_owner(skb, asoc);
+	sac->sac_assoc_id = sctp_assoc2id(asoc);
+
+	return event;
+
+fail:
+	return NULL;
+}
+
+/* Create and initialize an SCTP_PEER_ADDR_CHANGE event.
+ *
+ * Socket Extensions for SCTP - draft-01
+ * 5.3.1.2 SCTP_PEER_ADDR_CHANGE
+ *
+ * When a destination address on a multi-homed peer encounters a change
+ * an interface details event is sent.
+ */
+struct sctp_ulpevent *sctp_ulpevent_make_peer_addr_change(
+	const struct sctp_association *asoc,
+	const struct sockaddr_storage *aaddr,
+	int flags, int state, int error, int gfp)
+{
+	struct sctp_ulpevent *event;
+	struct sctp_paddr_change  *spc;
+	struct sk_buff *skb;
+
+	event = sctp_ulpevent_new(sizeof(struct sctp_paddr_change),
+				  MSG_NOTIFICATION, gfp);
+	if (!event)
+		goto fail;
+
+	skb = sctp_event2skb(event);
+	spc = (struct sctp_paddr_change *)
+		skb_put(skb, sizeof(struct sctp_paddr_change));
+
+	/* Sockets API Extensions for SCTP
+	 * Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE
+	 *
+	 * spc_type:
+	 *
+	 *    It should be SCTP_PEER_ADDR_CHANGE.
+	 */
+	spc->spc_type = SCTP_PEER_ADDR_CHANGE;
+
+	/* Sockets API Extensions for SCTP
+	 * Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE
+	 *
+	 * spc_length: sizeof (__u32)
+	 *
+	 * This field is the total length of the notification data, including
+	 * the notification header.
+	 */
+	spc->spc_length = sizeof(struct sctp_paddr_change);
+
+	/* Sockets API Extensions for SCTP
+	 * Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE
+	 *
+	 * spc_flags: 16 bits (unsigned integer)
+	 * Currently unused.
+	 */
+	spc->spc_flags = 0;
+
+	/* Sockets API Extensions for SCTP
+	 * Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE
+	 *
+	 * spc_state:  32 bits (signed integer)
+	 *
+	 * This field holds one of a number of values that communicate the
+	 * event that happened to the address.
+	 */
+	spc->spc_state = state;
+
+	/* Sockets API Extensions for SCTP
+	 * Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE
+	 *
+	 * spc_error:  32 bits (signed integer)
+	 *
+	 * If the state was reached due to any error condition (e.g.
+	 * ADDRESS_UNREACHABLE) any relevant error information is available in
+	 * this field.
+	 */
+	spc->spc_error = error;
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.1 SCTP_ASSOC_CHANGE
+	 *
+	 * spc_assoc_id: sizeof (sctp_assoc_t)
+	 *
+	 * The association id field, holds the identifier for the association.
+	 * All notifications for a given association have the same association
+	 * identifier.  For TCP style socket, this field is ignored.
+	 */
+	sctp_ulpevent_set_owner(skb, asoc);
+	spc->spc_assoc_id = sctp_assoc2id(asoc);
+
+	/* Sockets API Extensions for SCTP
+	 * Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE
+	 *
+	 * spc_aaddr: sizeof (struct sockaddr_storage)
+	 *
+	 * The affected address field, holds the remote peer's address that is
+	 * encountering the change of state.
+	 */
+	memcpy(&spc->spc_aaddr, aaddr, sizeof(struct sockaddr_storage));
+
+	return event;
+
+fail:
+	return NULL;
+}
+
+/* Create and initialize an SCTP_REMOTE_ERROR notification.
+ *
+ * Note: This assumes that the chunk->skb->data already points to the
+ * operation error payload.
+ *
+ * Socket Extensions for SCTP - draft-01
+ * 5.3.1.3 SCTP_REMOTE_ERROR
+ *
+ * A remote peer may send an Operational Error message to its peer.
+ * This message indicates a variety of error conditions on an
+ * association. The entire error TLV as it appears on the wire is
+ * included in a SCTP_REMOTE_ERROR event.  Please refer to the SCTP
+ * specification [SCTP] and any extensions for a list of possible
+ * error formats.
+ */
+struct sctp_ulpevent *sctp_ulpevent_make_remote_error(
+	const struct sctp_association *asoc, sctp_chunk_t *chunk,
+	__u16 flags, int gfp)
+{
+	struct sctp_ulpevent *event;
+	struct sctp_remote_error *sre;
+	struct sk_buff *skb;
+	sctp_errhdr_t *ch;
+	__u16 cause;
+	int elen;
+
+	ch = (sctp_errhdr_t *)(chunk->skb->data);
+	cause = ch->cause;
+	elen = ntohs(ch->length) - sizeof(sctp_errhdr_t);
+
+	/* Pull off the ERROR header.  */
+	skb_pull(chunk->skb, sizeof(sctp_errhdr_t));
+
+	/* Copy the skb to a new skb with room for us to prepend
+	 * notification with.
+	 */
+	skb = skb_copy_expand(chunk->skb,
+			      sizeof(struct sctp_remote_error), /* headroom */
+			      0,                                /* tailroom */
+			      gfp);
+
+	/* Pull off the rest of the cause TLV from the chunk.  */
+	skb_pull(chunk->skb, elen);
+	if (!skb)
+		goto fail;
+
+	/* Embed the event fields inside the cloned skb.  */
+	event = sctp_skb2event(skb);
+	event = sctp_ulpevent_init(event, MSG_NOTIFICATION);
+
+	if (!event)
+		goto fail;
+
+	sre = (struct sctp_remote_error *)
+		skb_push(skb, sizeof(struct sctp_remote_error));
+
+	/* Trim the buffer to the right length.  */
+	skb_trim(skb, sizeof(struct sctp_remote_error) + elen);
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.3 SCTP_REMOTE_ERROR
+	 *
+	 * sre_type:
+	 *   It should be SCTP_REMOTE_ERROR.
+	 */
+	sre->sre_type = SCTP_REMOTE_ERROR;
+
+	/*
+	 * Socket Extensions for SCTP
+	 * 5.3.1.3 SCTP_REMOTE_ERROR
+	 *
+	 * sre_flags: 16 bits (unsigned integer)
+	 *   Currently unused.
+	 */
+	sre->sre_flags = 0;
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.3 SCTP_REMOTE_ERROR
+	 *
+	 * sre_length: sizeof (__u32)
+	 *
+	 * This field is the total length of the notification data,
+	 * including the notification header.
+	 */
+	sre->sre_length = skb->len;
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.3 SCTP_REMOTE_ERROR
+	 *
+	 * sre_error: 16 bits (unsigned integer)
+	 * This value represents one of the Operational Error causes defined in
+	 * the SCTP specification, in network byte order.
+	 */
+	sre->sre_error = cause;
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.3 SCTP_REMOTE_ERROR
+	 *
+	 * sre_assoc_id: sizeof (sctp_assoc_t)
+	 *
+	 * The association id field, holds the identifier for the association.
+	 * All notifications for a given association have the same association
+	 * identifier.  For TCP style socket, this field is ignored.
+	 */
+	skb = sctp_event2skb(event);
+	sctp_ulpevent_set_owner(skb, asoc);
+	sre->sre_assoc_id = sctp_assoc2id(asoc);
+
+	return event;
+
+fail:
+	return NULL;
+}
+
+/* Create and initialize a SCTP_SEND_FAILED notification.
+ *
+ * Socket Extensions for SCTP - draft-01
+ * 5.3.1.4 SCTP_SEND_FAILED
+ */
+struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
+	const struct sctp_association *asoc, sctp_chunk_t *chunk,
+	__u16 flags, __u32 error, int gfp)
+{
+	struct sctp_ulpevent *event;
+	struct sctp_send_failed *ssf;
+	struct sk_buff *skb;
+
+	/* Make skb with more room so we can prepend notification.  */
+	skb = skb_copy_expand(chunk->skb,
+			      sizeof(struct sctp_send_failed), /* headroom */
+			      0,                               /* tailroom */
+			      gfp);
+	if (!skb)
+		goto fail;
+
+	/* Pull off the common chunk header and DATA header.  */
+	skb_pull(skb, sizeof(sctp_data_chunk_t));
+
+	/* Embed the event fields inside the cloned skb.  */
+	event = sctp_skb2event(skb);
+	event = sctp_ulpevent_init(event, MSG_NOTIFICATION);
+	if (!event)
+		goto fail;
+
+	ssf = (struct sctp_send_failed *)
+		skb_push(skb, sizeof(struct sctp_send_failed));
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.4 SCTP_SEND_FAILED
+	 *
+	 * ssf_type:
+	 * It should be SCTP_SEND_FAILED.
+	 */
+	ssf->ssf_type = SCTP_SEND_FAILED;
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.4 SCTP_SEND_FAILED
+	 *
+	 * ssf_flags: 16 bits (unsigned integer)
+	 * The flag value will take one of the following values
+	 *
+	 * SCTP_DATA_UNSENT - Indicates that the data was never put on
+	 *                    the wire.
+	 *
+	 * SCTP_DATA_SENT   - Indicates that the data was put on the wire.
+	 *                    Note that this does not necessarily mean that the
+	 *                    data was (or was not) successfully delivered.
+	 */
+	ssf->ssf_flags = flags;
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.4 SCTP_SEND_FAILED
+	 *
+	 * ssf_length: sizeof (__u32)
+	 * This field is the total length of the notification data, including
+	 * the notification header.
+	 */
+	ssf->ssf_length = skb->len;
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.4 SCTP_SEND_FAILED
+	 *
+	 * ssf_error: 16 bits (unsigned integer)
+	 * This value represents the reason why the send failed, and if set,
+	 * will be a SCTP protocol error code as defined in [SCTP] section
+	 * 3.3.10.
+	 */
+	ssf->ssf_error = error;
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.4 SCTP_SEND_FAILED
+	 *
+	 * ssf_info: sizeof (struct sctp_sndrcvinfo)
+	 * The original send information associated with the undelivered
+	 * message.
+	 */
+	memcpy(&ssf->ssf_info, &chunk->sinfo, sizeof(struct sctp_sndrcvinfo));
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.4 SCTP_SEND_FAILED
+	 *
+	 * ssf_assoc_id: sizeof (sctp_assoc_t)
+	 * The association id field, sf_assoc_id, holds the identifier for the
+	 * association.  All notifications for a given association have the
+	 * same association identifier.  For TCP style socket, this field is
+	 * ignored.
+	 */
+	skb = sctp_event2skb(event);
+	sctp_ulpevent_set_owner(skb, asoc);
+	ssf->ssf_assoc_id = sctp_assoc2id(asoc);
+	return event;
+
+fail:
+	return NULL;
+}
+
+/* Create and initialize a SCTP_SHUTDOWN_EVENT notification.
+ *
+ * Socket Extensions for SCTP - draft-01
+ * 5.3.1.5 SCTP_SHUTDOWN_EVENT
+ */
+struct sctp_ulpevent *sctp_ulpevent_make_shutdown_event(
+	const struct sctp_association *asoc,
+	__u16 flags, int gfp)
+{
+	struct sctp_ulpevent *event;
+	struct sctp_shutdown_event *sse;
+	struct sk_buff *skb;
+
+	event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change),
+				  MSG_NOTIFICATION, gfp);
+	if (!event)
+		goto fail;
+
+	skb = sctp_event2skb(event);
+	sse = (struct sctp_shutdown_event *)
+		skb_put(skb, sizeof(struct sctp_shutdown_event));
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.5 SCTP_SHUTDOWN_EVENT
+	 *
+	 * sse_type
+	 * It should be SCTP_SHUTDOWN_EVENT
+	 */
+	sse->sse_type = SCTP_SHUTDOWN_EVENT;
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.5 SCTP_SHUTDOWN_EVENT
+	 *
+	 * sse_flags: 16 bits (unsigned integer)
+	 * Currently unused.
+	 */
+	sse->sse_flags = 0;
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.5 SCTP_SHUTDOWN_EVENT
+	 *
+	 * sse_length: sizeof (__u32)
+	 * This field is the total length of the notification data, including
+	 * the notification header.
+	 */
+	sse->sse_length = sizeof(struct sctp_shutdown_event);
+
+	/* Socket Extensions for SCTP
+	 * 5.3.1.5 SCTP_SHUTDOWN_EVENT
+	 *
+	 * sse_assoc_id: sizeof (sctp_assoc_t)
+	 * The association id field, holds the identifier for the association.
+	 * All notifications for a given association have the same association
+	 * identifier.  For TCP style socket, this field is ignored.
+	 */
+	sctp_ulpevent_set_owner(skb, asoc);
+	sse->sse_assoc_id = sctp_assoc2id(asoc);
+
+	return event;
+
+fail:
+	return NULL;
+}
+
+/* A message has been received.  Package this message as a notification
+ * to pass it to the upper layers.  Go ahead and calculate the sndrcvinfo
+ * even if filtered out later.
+ *
+ * Socket Extensions for SCTP
+ * 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
+ */
+struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
+		   sctp_chunk_t *chunk, int gfp)
+{
+	struct sctp_ulpevent *event;
+	struct sctp_sndrcvinfo *info;
+	struct sk_buff *skb, *list;
+	size_t padding, len;
+
+	/* Clone the original skb, sharing the data.  */
+	skb = skb_clone(chunk->skb, gfp);
+	if (!skb)
+		goto fail;
+
+	/* First calculate the padding, so we don't inadvertently
+	 * pass up the wrong length to the user.
+	 *
+	 * RFC 2960 - Section 3.2  Chunk Field Descriptions
+	 *
+	 * The total length of a chunk(including Type, Length and Value fields)
+	 * MUST be a multiple of 4 bytes.  If the length of the chunk is not a
+	 * multiple of 4 bytes, the sender MUST pad the chunk with all zero
+	 * bytes and this padding is not included in the chunk length field.
+	 * The sender should never pad with more than 3 bytes.  The receiver
+	 * MUST ignore the padding bytes.
+	 */
+	len = ntohs(chunk->chunk_hdr->length);
+	padding = WORD_ROUND(len) - len;
+
+	/* Fixup cloned skb with just this chunks data.  */
+	skb_trim(skb, chunk->chunk_end - padding - skb->data);
+
+	/* Set up a destructor to do rwnd accounting.  */
+	sctp_ulpevent_set_owner_r(skb, asoc);
+
+	/* Embed the event fields inside the cloned skb.  */
+	event = sctp_skb2event(skb);
+
+	/* Initialize event with flags 0.  */
+	event = sctp_ulpevent_init(event, 0);
+	if (!event)
+		goto fail_init;
+
+	event->iif = sctp_chunk_iif(chunk);
+	/* Note:  Not clearing the entire event struct as
+	 * this is just a fragment of the real event.  However,
+	 * we still need to do rwnd accounting.
+	 */
+	for (list = skb_shinfo(skb)->frag_list; list; list = list->next)
+		sctp_ulpevent_set_owner_r(list, asoc);
+
+	info = (struct sctp_sndrcvinfo *) &event->sndrcvinfo;
+
+	/* Sockets API Extensions for SCTP
+	 * Section 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
+	 *
+	 * sinfo_stream: 16 bits (unsigned integer)
+	 *
+	 * For recvmsg() the SCTP stack places the message's stream number in
+	 * this value.
+	 */
+	info->sinfo_stream = ntohs(chunk->subh.data_hdr->stream);
+
+	/* Sockets API Extensions for SCTP
+	 * Section 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
+	 *
+	 * sinfo_ssn: 16 bits (unsigned integer)
+	 *
+	 * For recvmsg() this value contains the stream sequence number that
+	 * the remote endpoint placed in the DATA chunk.  For fragmented
+	 * messages this is the same number for all deliveries of the message
+	 * (if more than one recvmsg() is needed to read the message).
+	 */
+	info->sinfo_ssn = ntohs(chunk->subh.data_hdr->ssn);
+
+        /* Sockets API Extensions for SCTP
+	 * Section 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
+	 *
+	 * sinfo_ppid: 32 bits (unsigned integer)
+	 *
+	 * In recvmsg() this value is
+	 * the same information that was passed by the upper layer in the peer
+	 * application.  Please note that byte order issues are NOT accounted
+	 * for and this information is passed opaquely by the SCTP stack from
+	 * one end to the other.
+	 */
+	info->sinfo_ppid = ntohl(chunk->subh.data_hdr->ppid);
+
+	/* Sockets API Extensions for SCTP
+	 * Section 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
+	 *
+	 * sinfo_flags: 16 bits (unsigned integer)
+	 *
+	 * This field may contain any of the following flags and is composed of
+	 * a bitwise OR of these values.
+	 *
+	 * recvmsg() flags:
+	 *
+	 * MSG_UNORDERED - This flag is present when the message was sent
+	 *                 non-ordered.
+	 */
+	if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) {
+		info->sinfo_flags |= MSG_UNORDERED;
+
+		/* sinfo_cumtsn: 32 bit (unsigned integer)
+		 *
+		 * This field will hold the current cumulative TSN as
+		 * known by the underlying SCTP layer.  Note this field is
+		 * ignored when sending and only valid for a receive
+		 * operation when sinfo_flags are set to MSG_UNORDERED.
+		 */
+		info->sinfo_cumtsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map);
+	}
+
+	/* Note:  For reassembly, we need to have the fragmentation bits.
+	 * For now, merge these into the msg_flags, since those bit
+	 * possitions are not used.
+	 */
+	event->msg_flags |= chunk->chunk_hdr->flags;
+
+	/* With 04 draft, tsn moves into sndrcvinfo. */
+	info->sinfo_tsn = ntohl(chunk->subh.data_hdr->tsn);
+
+	/* Context is not used on receive. */
+	info->sinfo_context = 0;
+
+	/* Sockets API Extensions for SCTP
+	 * Section 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
+	 *
+	 * sinfo_assoc_id: sizeof (sctp_assoc_t)
+	 *
+	 * The association handle field, sinfo_assoc_id, holds the identifier
+	 * for the association announced in the COMMUNICATION_UP notification.
+	 * All notifications for a given association have the same identifier.
+	 * Ignored for TCP-style sockets.
+	 */
+	info->sinfo_assoc_id = sctp_assoc2id(asoc);
+
+	return event;
+
+fail_init:
+	kfree_skb(skb);
+
+fail:
+	return NULL;
+}
+
+/* Create a partial delivery related event.
+ *
+ * 5.3.1.7 SCTP_PARTIAL_DELIVERY_EVENT
+ *
+ *   When a reciever is engaged in a partial delivery of a
+ *   message this notification will be used to inidicate
+ *   various events.
+ */
+struct sctp_ulpevent *sctp_ulpevent_make_pdapi(
+	const struct sctp_association *asoc, __u32 indication, int gfp)
+{
+	struct sctp_ulpevent *event;
+	struct sctp_rcv_pdapi_event *pd;
+	struct sk_buff *skb;
+
+	event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change),
+				  MSG_NOTIFICATION, gfp);
+	if (!event)
+		goto fail;
+
+	skb = sctp_event2skb(event);
+	pd = (struct sctp_rcv_pdapi_event *)
+		skb_put(skb, sizeof(struct sctp_rcv_pdapi_event));
+
+	/* pdapi_type
+	 *   It should be SCTP_PARTIAL_DELIVERY_EVENT
+	 *
+	 * pdapi_flags: 16 bits (unsigned integer)
+	 *   Currently unused.
+	 */
+	pd->pdapi_type = SCTP_PARTIAL_DELIVERY_EVENT;
+	pd->pdapi_flags = 0;
+
+	/* pdapi_length: 32 bits (unsigned integer)
+	 *
+	 * This field is the total length of the notification data, including
+	 * the notification header.  It will generally be sizeof (struct
+	 * sctp_rcv_pdapi_event).
+	 */
+	pd->pdapi_length = sizeof(struct sctp_rcv_pdapi_event);
+
+        /*  pdapi_indication: 32 bits (unsigned integer)
+	 *
+	 * This field holds the indication being sent to the application.
+	 */
+	pd->pdapi_indication = indication;
+
+	/*  pdapi_assoc_id: sizeof (sctp_assoc_t)
+	 *
+	 * The association id field, holds the identifier for the association.
+	 */
+	pd->pdapi_assoc_id = sctp_assoc2id(asoc);
+
+	return event;
+fail:
+	return NULL;
+}
+
+/* Return the notification type, assuming this is a notification
+ * event.
+ */
+__u16 sctp_ulpevent_get_notification_type(const struct sctp_ulpevent *event)
+{
+	union sctp_notification *notification;
+	struct sk_buff *skb;
+
+	skb = sctp_event2skb((struct sctp_ulpevent *)event);
+	notification = (union sctp_notification *) skb->data;
+	return notification->h.sn_type;
+}
+
+/* Copy out the sndrcvinfo into a msghdr.  */
+void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event,
+				   struct msghdr *msghdr)
+{
+	if (!sctp_ulpevent_is_notification(event)) {
+		put_cmsg(msghdr, IPPROTO_SCTP, SCTP_SNDRCV,
+			 sizeof(struct sctp_sndrcvinfo),
+			 (void *) &event->sndrcvinfo);
+	}
+}
+
+/* Do accounting for bytes just read by user.  */
+static void sctp_rcvmsg_rfree(struct sk_buff *skb)
+{
+	struct sctp_association *asoc;
+	struct sctp_ulpevent *event;
+
+	/* Current stack structures assume that the rcv buffer is
+	 * per socket.   For UDP style sockets this is not true as
+	 * multiple associations may be on a single UDP-style socket.
+	 * Use the local private area of the skb to track the owning
+	 * association.
+	 */
+	event = sctp_skb2event(skb);
+	asoc = event->event_asoc;
+	sctp_assoc_rwnd_increase(asoc, skb_headlen(skb));
+	sctp_association_put(asoc);
+}
+
+/* Charge receive window for bytes received.  */
+static void sctp_ulpevent_set_owner_r(struct sk_buff *skb,
+				      struct sctp_association *asoc)
+{
+	struct sctp_ulpevent *event;
+
+	/* The current stack structures assume that the rcv buffer is
+	 * per socket.  For UDP-style sockets this is not true as
+	 * multiple associations may be on a single UDP-style socket.
+	 * We use the local private area of the skb to track the owning
+	 * association.
+	 */
+	sctp_association_hold(asoc);
+	skb->sk = asoc->base.sk;
+	event = sctp_skb2event(skb);
+	event->event_asoc = asoc;
+
+	skb->destructor = sctp_rcvmsg_rfree;
+
+	sctp_assoc_rwnd_decrease(asoc, skb_headlen(skb));
+}
+
+/* A simple destructor to give up the reference to the association. */
+static void sctp_ulpevent_rfree(struct sk_buff *skb)
+{
+	struct sctp_ulpevent *event;
+
+	event = sctp_skb2event(skb);
+	sctp_association_put(event->event_asoc);
+}
+
+/* Hold the association in case the msg_name needs read out of
+ * the association.
+ */
+static void sctp_ulpevent_set_owner(struct sk_buff *skb,
+				    const struct sctp_association *asoc)
+{
+	struct sctp_ulpevent *event;
+
+	/* Cast away the const, as we are just wanting to
+	 * bump the reference count.
+	 */
+	sctp_association_hold((struct sctp_association *)asoc);
+	skb->sk = asoc->base.sk;
+	event = sctp_skb2event(skb);
+	event->event_asoc = (struct sctp_association *)asoc;
+	skb->destructor = sctp_ulpevent_rfree;
+}
diff -urN linux-2.4.23-pre5/net/sctp/ulpqueue.c linux-2.4.23-pre6/net/sctp/ulpqueue.c
--- linux-2.4.23-pre5/net/sctp/ulpqueue.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.23-pre6/net/sctp/ulpqueue.c	2003-10-01 10:38:59.000000000 -0700
@@ -0,0 +1,820 @@
+/* SCTP kernel reference Implementation
+ * Copyright (c) 1999-2000 Cisco, Inc.
+ * Copyright (c) 1999-2001 Motorola, Inc.
+ * Copyright (c) 2001-2003 International Business Machines, Corp.
+ * Copyright (c) 2001 Intel Corp.
+ * Copyright (c) 2001 Nokia, Inc.
+ * Copyright (c) 2001 La Monte H.P. Yarroll
+ *
+ * This abstraction carries sctp events to the ULP (sockets).
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The SCTP reference implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email address(es):
+ *    lksctp developers <lksctp-developers@lists.sourceforge.net>
+ *
+ * Or submit a bug report through the following website:
+ *    http://www.sf.net/projects/lksctp
+ *
+ * Written or modified by:
+ *    Jon Grimm             <jgrimm@us.ibm.com>
+ *    La Monte H.P. Yarroll <piggy@acm.org>
+ *    Sridhar Samudrala     <sri@us.ibm.com>
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/sctp/structs.h>
+#include <net/sctp/sctp.h>
+#include <net/sctp/sm.h>
+
+/* Forward declarations for internal helpers.  */
+static inline struct sctp_ulpevent * sctp_ulpq_reasm(struct sctp_ulpq *ulpq,
+						     struct sctp_ulpevent *);
+static inline struct sctp_ulpevent *sctp_ulpq_order(struct sctp_ulpq *,
+						    struct sctp_ulpevent *);
+
+/* 1st Level Abstractions */
+
+/* Create a new ULP queue.  */
+struct sctp_ulpq *sctp_ulpq_new(struct sctp_association *asoc, int gfp)
+{
+	struct sctp_ulpq *ulpq;
+
+	ulpq = kmalloc(sizeof(struct sctp_ulpq), gfp);
+	if (!ulpq)
+		goto fail;
+	if (!sctp_ulpq_init(ulpq, asoc))
+		goto fail_init;
+	ulpq->malloced = 1;
+	return ulpq;
+
+fail_init:
+	kfree(ulpq);
+fail:
+	return NULL;
+}
+
+/* Initialize a ULP queue from a block of memory.  */
+struct sctp_ulpq *sctp_ulpq_init(struct sctp_ulpq *ulpq,
+				 struct sctp_association *asoc)
+{
+	memset(ulpq, sizeof(struct sctp_ulpq), 0x00);
+
+	ulpq->asoc = asoc;
+	skb_queue_head_init(&ulpq->reasm);
+	skb_queue_head_init(&ulpq->lobby);
+	ulpq->pd_mode  = 0;
+	ulpq->malloced = 0;
+
+	return ulpq;
+}
+
+
+/* Flush the reassembly and ordering queues.  */
+void sctp_ulpq_flush(struct sctp_ulpq *ulpq)
+{
+	struct sk_buff *skb;
+	struct sctp_ulpevent *event;
+
+	while ((skb = __skb_dequeue(&ulpq->lobby))) {
+		event = sctp_skb2event(skb);
+		sctp_ulpevent_free(event);
+	}
+
+	while ((skb = __skb_dequeue(&ulpq->reasm))) {
+		event = sctp_skb2event(skb);
+		sctp_ulpevent_free(event);
+	}
+
+}
+
+/* Dispose of a ulpqueue.  */
+void sctp_ulpq_free(struct sctp_ulpq *ulpq)
+{
+	sctp_ulpq_flush(ulpq);
+	if (ulpq->malloced)
+		kfree(ulpq);
+}
+
+/* Process an incoming DATA chunk.  */
+int sctp_ulpq_tail_data(struct sctp_ulpq *ulpq, sctp_chunk_t *chunk,
+			int gfp)
+{
+	struct sk_buff_head temp;
+	sctp_data_chunk_t *hdr;
+	struct sctp_ulpevent *event;
+
+	hdr = (sctp_data_chunk_t *) chunk->chunk_hdr;
+
+	/* Create an event from the incoming chunk. */
+	event = sctp_ulpevent_make_rcvmsg(chunk->asoc, chunk, gfp);
+	if (!event)
+		return -ENOMEM;
+
+	/* Do reassembly if needed.  */
+	event = sctp_ulpq_reasm(ulpq, event);
+
+	/* Do ordering if needed.  */
+	if ((event) && (event->msg_flags & MSG_EOR)){
+		/* Create a temporary list to collect chunks on.  */
+		skb_queue_head_init(&temp);
+		__skb_queue_tail(&temp, sctp_event2skb(event));
+
+		event = sctp_ulpq_order(ulpq, event);
+	}
+
+	/* Send event to the ULP.  */
+	if (event)
+		sctp_ulpq_tail_event(ulpq, event);
+
+	return 0;
+}
+
+/* Add a new event for propagation to the ULP.  */
+/* 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)
+{
+	struct sctp_opt *sp;
+	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->receive_queue);
+		list = (struct list_head *)&sctp_sk(sk)->pd_lobby;
+		INIT_LIST_HEAD(list);
+		return 1;
+	}
+	return 0;
+}
+
+/* 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);
+}
+
+
+
+int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event)
+{
+	struct sock *sk = ulpq->asoc->base.sk;
+	struct sk_buff_head *queue;
+	int clear_pd = 0;
+
+	/* If the socket is just going to throw this away, do not
+	 * even try to deliver it.
+	 */
+	if ((sk->dead) || (sk->shutdown & RCV_SHUTDOWN))
+		goto out_free;
+
+	/* Check if the user wishes to receive this event.  */
+	if (!sctp_ulpevent_is_enabled(event, &sctp_sk(sk)->subscribe))
+		goto out_free;
+
+	/* If we are in partial delivery mode, post to the lobby until
+	 * partial delivery is cleared, unless, of course _this_ is
+	 * the association the cause of the partial delivery.
+	 */
+
+	if (!sctp_sk(sk)->pd_mode) {
+		queue = &sk->receive_queue;
+	} else if (ulpq->pd_mode) {
+		if (event->msg_flags & MSG_NOTIFICATION)
+		       	queue = &sctp_sk(sk)->pd_lobby;
+		else {
+			clear_pd = event->msg_flags & MSG_EOR;
+			queue = &sk->receive_queue;
+		}
+	} else
+		queue = &sctp_sk(sk)->pd_lobby;
+
+
+	/* If we are harvesting multiple skbs they will be
+	 * collected on a list.
+	 */
+	if (sctp_event2skb(event)->list)
+		sctp_skb_list_tail(sctp_event2skb(event)->list, queue);
+	else
+		__skb_queue_tail(queue, sctp_event2skb(event));
+
+	/* Did we just complete partial delivery and need to get
+	 * rolling again?  Move pending data to the receive
+	 * queue.
+	 */
+	if (clear_pd)
+		sctp_ulpq_clear_pd(ulpq);
+
+	if (queue == &sk->receive_queue)
+		sk->data_ready(sk, 0);
+	return 1;
+
+out_free:
+	if (sctp_event2skb(event)->list)
+		skb_queue_purge(sctp_event2skb(event)->list);
+	else
+		kfree_skb(sctp_event2skb(event));
+	return 0;
+}
+
+/* 2nd Level Abstractions */
+
+/* Helper function to store chunks that need to be reassembled.  */
+static inline void sctp_ulpq_store_reasm(struct sctp_ulpq *ulpq,
+					 struct sctp_ulpevent *event)
+{
+	struct sk_buff *pos;
+	struct sctp_ulpevent *cevent;
+	__u32 tsn, ctsn;
+
+	tsn = event->sndrcvinfo.sinfo_tsn;
+
+	/* See if it belongs at the end. */
+	pos = skb_peek_tail(&ulpq->reasm);
+	if (!pos) {
+		__skb_queue_tail(&ulpq->reasm, sctp_event2skb(event));
+		return;
+	}
+
+	/* Short circuit just dropping it at the end. */
+	cevent = sctp_skb2event(pos);
+	ctsn = cevent->sndrcvinfo.sinfo_tsn;
+	if (TSN_lt(ctsn, tsn)) {
+		__skb_queue_tail(&ulpq->reasm, sctp_event2skb(event));
+		return;
+	}
+
+	/* Find the right place in this list. We store them by TSN.  */
+	skb_queue_walk(&ulpq->reasm, pos) {
+		cevent = sctp_skb2event(pos);
+		ctsn = cevent->sndrcvinfo.sinfo_tsn;
+
+		if (TSN_lt(tsn, ctsn))
+			break;
+	}
+
+	/* Insert before pos. */
+	__skb_insert(sctp_event2skb(event), pos->prev, pos, &ulpq->reasm);
+
+}
+
+/* Helper function to return an event corresponding to the reassembled
+ * datagram.
+ * This routine creates a re-assembled skb given the first and last skb's
+ * as stored in the reassembly queue. The skb's may be non-linear if the sctp
+ * payload was fragmented on the way and ip had to reassemble them.
+ * We add the rest of skb's to the first skb's fraglist.
+ */
+static inline struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff *f_frag, struct sk_buff *l_frag)
+{
+	struct sk_buff *pos;
+	struct sctp_ulpevent *event;
+	struct sk_buff *pnext, *last;
+	struct sk_buff *list = skb_shinfo(f_frag)->frag_list;
+
+	/* Store the pointer to the 2nd skb */
+	if (f_frag == l_frag)
+		pos = NULL;
+	else
+		pos = f_frag->next;
+
+	/* Get the last skb in the f_frag's frag_list if present. */
+	for (last = list; list; last = list, list = list->next);
+
+	/* Add the list of remaining fragments to the first fragments
+	 * frag_list.
+	 */
+	if (last)
+		last->next = pos;
+	else
+		skb_shinfo(f_frag)->frag_list = pos;
+
+	/* Remove the first fragment from the reassembly queue.  */
+	__skb_unlink(f_frag, f_frag->list);
+	while (pos) {
+
+		pnext = pos->next;
+
+		/* Update the len and data_len fields of the first fragment. */
+		f_frag->len += pos->len;
+		f_frag->data_len += pos->len;
+
+		/* Remove the fragment from the reassembly queue.  */
+		__skb_unlink(pos, pos->list);
+
+		/* Break if we have reached the last fragment.  */
+		if (pos == l_frag)
+			break;
+
+		pos->next = pnext;
+		pos = pnext;
+	};
+
+	event = sctp_skb2event(f_frag);
+	SCTP_INC_STATS(SctpReasmUsrMsgs);
+
+	return event;
+}
+
+
+/* Helper function to check if an incoming chunk has filled up the last
+ * missing fragment in a SCTP datagram and return the corresponding event.
+ */
+static inline struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_ulpq *ulpq)
+{
+	struct sk_buff *pos;
+	struct sctp_ulpevent *cevent;
+	struct sk_buff *first_frag = NULL;
+	__u32 ctsn, next_tsn;
+	struct sctp_ulpevent *retval = NULL;
+
+	/* Initialized to 0 just to avoid compiler warning message.  Will
+	 * never be used with this value. It is referenced only after it
+	 * is set when we find the first fragment of a message.
+	 */
+	next_tsn = 0;
+
+	/* The chunks are held in the reasm queue sorted by TSN.
+	 * Walk through the queue sequentially and look for a sequence of
+	 * fragmented chunks that complete a datagram.
+	 * 'first_frag' and next_tsn are reset when we find a chunk which
+	 * is the first fragment of a datagram. Once these 2 fields are set
+	 * 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.
+	 */
+	skb_queue_walk(&ulpq->reasm, pos) {
+		cevent = sctp_skb2event(pos);
+		ctsn = cevent->sndrcvinfo.sinfo_tsn;
+
+		switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
+		case SCTP_DATA_FIRST_FRAG:
+			first_frag = pos;
+			next_tsn = ctsn + 1;
+			break;
+
+		case SCTP_DATA_MIDDLE_FRAG:
+			if ((first_frag) && (ctsn == next_tsn))
+				next_tsn++;
+			else
+				first_frag = NULL;
+			break;
+
+		case SCTP_DATA_LAST_FRAG:
+			if (first_frag && (ctsn == next_tsn))
+				goto found;
+			else
+				first_frag = NULL;
+			break;
+		};
+
+	}
+done:
+	return retval;
+found:
+	retval = sctp_make_reassembled_event(first_frag, pos);
+	if (retval)
+		retval->msg_flags |= MSG_EOR;
+	goto done;
+}
+
+/* Retrieve the next set of fragments of a partial message. */
+static inline struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq *ulpq)
+{
+	struct sk_buff *pos, *last_frag, *first_frag;
+	struct sctp_ulpevent *cevent;
+	__u32 ctsn, next_tsn;
+	int is_last;
+	struct sctp_ulpevent *retval;
+
+	/* The chunks are held in the reasm queue sorted by TSN.
+	 * Walk through the queue sequentially and look for the first
+	 * sequence of fragmented chunks.
+	 */
+
+	if (skb_queue_empty(&ulpq->reasm))
+		return NULL;
+
+	last_frag = first_frag = NULL;
+	retval = NULL;
+	next_tsn = 0;
+	is_last = 0;
+
+	skb_queue_walk(&ulpq->reasm, pos) {
+		cevent = sctp_skb2event(pos);
+		ctsn = cevent->sndrcvinfo.sinfo_tsn;
+
+		switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
+		case SCTP_DATA_MIDDLE_FRAG:
+			if (!first_frag) {
+				first_frag = pos;
+				next_tsn = ctsn + 1;
+				last_frag = pos;
+			} else if (next_tsn == ctsn)
+				next_tsn++;
+			else
+				goto done;
+			break;
+		case SCTP_DATA_LAST_FRAG:
+			if (!first_frag)
+				first_frag = pos;
+			else if (ctsn != next_tsn)
+				goto done;
+			last_frag = pos;
+			is_last = 1;
+			goto done;
+		default:
+			return NULL;
+		};
+	}
+
+	/* We have the reassembled event. There is no need to look
+	 * further.
+	 */
+done:
+	retval = sctp_make_reassembled_event(first_frag, last_frag);
+	if (retval && is_last)
+		retval->msg_flags |= MSG_EOR;
+
+	return retval;
+}
+
+
+/* Helper function to reassemble chunks.  Hold chunks on the reasm queue that
+ * need reassembling.
+ */
+static inline struct sctp_ulpevent *sctp_ulpq_reasm(struct sctp_ulpq *ulpq,
+						   struct sctp_ulpevent *event)
+{
+	struct sctp_ulpevent *retval = NULL;
+
+	/* Check if this is part of a fragmented message.  */
+	if (SCTP_DATA_NOT_FRAG == (event->msg_flags & SCTP_DATA_FRAG_MASK)) {
+		event->msg_flags |= MSG_EOR;
+		return event;
+	}
+
+	sctp_ulpq_store_reasm(ulpq, event);
+	if (!ulpq->pd_mode)
+		retval = sctp_ulpq_retrieve_reassembled(ulpq);
+	else {
+		__u32 ctsn, ctsnap;
+
+		/* Do not even bother unless this is the next tsn to
+		 * be delivered.
+		 */
+		ctsn = event->sndrcvinfo.sinfo_tsn;
+		ctsnap = sctp_tsnmap_get_ctsn(&ulpq->asoc->peer.tsn_map);
+		if (TSN_lte(ctsn, ctsnap))
+			retval = sctp_ulpq_retrieve_partial(ulpq);
+	}
+
+	return retval;
+}
+
+/* Retrieve the first part (sequential fragments) for partial delivery.  */
+static inline struct sctp_ulpevent *sctp_ulpq_retrieve_first(struct sctp_ulpq *ulpq)
+{
+	struct sk_buff *pos, *last_frag, *first_frag;
+	struct sctp_ulpevent *cevent;
+	__u32 ctsn, next_tsn;
+	struct sctp_ulpevent *retval;
+
+	/* The chunks are held in the reasm queue sorted by TSN.
+	 * Walk through the queue sequentially and look for a sequence of
+	 * fragmented chunks that start a datagram.
+	 */
+
+	if (skb_queue_empty(&ulpq->reasm))
+		return NULL;
+
+	last_frag = first_frag = NULL;
+	retval = NULL;
+	next_tsn = 0;
+
+	skb_queue_walk(&ulpq->reasm, pos) {
+		cevent = sctp_skb2event(pos);
+		ctsn = cevent->sndrcvinfo.sinfo_tsn;
+
+		switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
+		case SCTP_DATA_FIRST_FRAG:
+			if (!first_frag) {
+				first_frag = pos;
+				next_tsn = ctsn + 1;
+				last_frag = pos;
+			} else
+				goto done;
+			break;
+
+		case SCTP_DATA_MIDDLE_FRAG:
+			if (!first_frag)
+				return NULL;
+			if (ctsn == next_tsn) {
+				next_tsn++;
+				last_frag = pos;
+			} else
+				goto done;
+			break;
+		default:
+			return NULL;
+		};
+	}
+
+	/* We have the reassembled event. There is no need to look
+	 * further.
+	 */
+done:
+	retval = sctp_make_reassembled_event(first_frag, last_frag);
+	return retval;
+}
+
+/* Helper function to gather skbs that have possibly become
+ * ordered by an an incoming chunk.
+ */
+static inline void sctp_ulpq_retrieve_ordered(struct sctp_ulpq *ulpq,
+					      struct sctp_ulpevent *event)
+{
+	struct sk_buff *pos, *tmp;
+	struct sctp_ulpevent *cevent;
+	struct sctp_stream *in;
+	__u16 sid, csid;
+	__u16 ssn, cssn;
+
+	sid = event->sndrcvinfo.sinfo_stream;
+	ssn = event->sndrcvinfo.sinfo_ssn;
+	in  = &ulpq->asoc->ssnmap->in;
+
+	/* We are holding the chunks by stream, by SSN.  */
+	sctp_skb_for_each(pos, &ulpq->lobby, tmp) {
+		cevent = sctp_skb2event(pos);
+		csid = cevent->sndrcvinfo.sinfo_stream;
+		cssn = cevent->sndrcvinfo.sinfo_ssn;
+
+		/* Have we gone too far?  */
+		if (csid > sid)
+			break;
+
+		/* Have we not gone far enough?  */
+		if (csid < sid)
+			continue;
+
+		if (cssn != sctp_ssn_peek(in, sid))
+			break;
+
+		/* Found it, so mark in the ssnmap. */
+		sctp_ssn_next(in, sid);
+
+		__skb_unlink(pos, pos->list);
+
+		/* Attach all gathered skbs to the event.  */
+		__skb_queue_tail(sctp_event2skb(event)->list, pos);
+	}
+}
+
+/* Helper function to store chunks needing ordering.  */
+static inline void sctp_ulpq_store_ordered(struct sctp_ulpq *ulpq,
+					   struct sctp_ulpevent *event)
+{
+	struct sk_buff *pos;
+	struct sctp_ulpevent *cevent;
+	__u16 sid, csid;
+	__u16 ssn, cssn;
+
+	pos = skb_peek_tail(&ulpq->lobby);
+	if (!pos) {
+		__skb_queue_tail(&ulpq->lobby, sctp_event2skb(event));
+		return;
+	}
+
+	sid = event->sndrcvinfo.sinfo_stream;
+	ssn = event->sndrcvinfo.sinfo_ssn;
+	
+	cevent = sctp_skb2event(pos);
+	csid = cevent->sndrcvinfo.sinfo_stream;
+	cssn = cevent->sndrcvinfo.sinfo_ssn;
+	if (sid > csid) {
+		__skb_queue_tail(&ulpq->lobby, sctp_event2skb(event));
+		return;
+	}
+
+	if ((sid == csid) && SSN_lt(cssn, ssn)) {
+		__skb_queue_tail(&ulpq->lobby, sctp_event2skb(event));
+		return;
+	}
+
+	/* Find the right place in this list.  We store them by
+	 * stream ID and then by SSN.
+	 */
+	skb_queue_walk(&ulpq->lobby, pos) {
+		cevent = sctp_skb2event(pos);
+		csid = cevent->sndrcvinfo.sinfo_stream;
+		cssn = cevent->sndrcvinfo.sinfo_ssn;
+
+		if (csid > sid)
+			break;
+		if (csid == sid && SSN_lt(ssn, cssn))
+			break;
+	}
+
+
+	/* Insert before pos. */
+	__skb_insert(sctp_event2skb(event), pos->prev, pos, &ulpq->lobby);
+
+}
+
+static inline struct sctp_ulpevent *sctp_ulpq_order(struct sctp_ulpq *ulpq,
+					struct sctp_ulpevent *event)
+{
+	__u16 sid, ssn;
+	struct sctp_stream *in;
+
+	/* Check if this message needs ordering.  */
+	if (SCTP_DATA_UNORDERED & event->msg_flags)
+		return event;
+
+	/* Note: The stream ID must be verified before this routine.  */
+	sid = event->sndrcvinfo.sinfo_stream;
+	ssn = event->sndrcvinfo.sinfo_ssn;
+	in  = &ulpq->asoc->ssnmap->in;
+
+	/* Is this the expected SSN for this stream ID?  */
+	if (ssn != sctp_ssn_peek(in, sid)) {
+		/* We've received something out of order, so find where it
+		 * needs to be placed.  We order by stream and then by SSN.
+		 */
+		sctp_ulpq_store_ordered(ulpq, event);
+		return NULL;
+	}
+
+	/* Mark that the next chunk has been found.  */
+	sctp_ssn_next(in, sid);
+
+	/* Go find any other chunks that were waiting for
+	 * ordering.
+	 */
+	sctp_ulpq_retrieve_ordered(ulpq, event);
+
+	return event;
+}
+
+/* Renege 'needed' bytes from the ordering queue. */
+static __u16 sctp_ulpq_renege_order(struct sctp_ulpq *ulpq, __u16 needed)
+{
+	__u16 freed = 0;
+	__u32 tsn;
+	struct sk_buff *skb;
+	struct sctp_ulpevent *event;
+	struct sctp_tsnmap *tsnmap;
+
+	tsnmap = &ulpq->asoc->peer.tsn_map;
+
+	while ((skb = __skb_dequeue_tail(&ulpq->lobby))) {
+		freed += skb_headlen(skb);
+		event = sctp_skb2event(skb);
+		tsn = event->sndrcvinfo.sinfo_tsn;
+
+		sctp_ulpevent_free(event);
+		sctp_tsnmap_renege(tsnmap, tsn);
+		if (freed >= needed)
+			return freed;
+	}
+
+	return freed;
+}
+
+/* Renege 'needed' bytes from the reassembly queue. */
+static __u16 sctp_ulpq_renege_frags(struct sctp_ulpq *ulpq, __u16 needed)
+{
+	__u16 freed = 0;
+	__u32 tsn;
+	struct sk_buff *skb;
+	struct sctp_ulpevent *event;
+	struct sctp_tsnmap *tsnmap;
+
+	tsnmap = &ulpq->asoc->peer.tsn_map;
+
+	/* Walk backwards through the list, reneges the newest tsns. */
+	while ((skb = __skb_dequeue_tail(&ulpq->reasm))) {
+		freed += skb_headlen(skb);
+		event = sctp_skb2event(skb);
+		tsn = event->sndrcvinfo.sinfo_tsn;
+
+		sctp_ulpevent_free(event);
+		sctp_tsnmap_renege(tsnmap, tsn);
+		if (freed >= needed)
+			return freed;
+	}
+
+	return freed;
+}
+
+/* Partial deliver the first message as there is pressure on rwnd. */
+void sctp_ulpq_partial_delivery(struct sctp_ulpq *ulpq,
+				struct sctp_chunk *chunk, int gfp)
+{
+	struct sctp_ulpevent *event;
+	struct sctp_association *asoc;
+
+	asoc = ulpq->asoc;
+
+	/* Are we already in partial delivery mode?  */
+	if (!sctp_sk(asoc->base.sk)->pd_mode) {
+
+		/* 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;
+			return;
+		}
+	}
+}
+
+/* Renege some packets to make room for an incoming chunk.  */
+void sctp_ulpq_renege(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
+		      int gfp)
+{
+	struct sctp_association *asoc;
+	__u16 needed, freed;
+
+	asoc = ulpq->asoc;
+
+	if (chunk) {
+		needed = ntohs(chunk->chunk_hdr->length);
+		needed -= sizeof(sctp_data_chunk_t);
+	} else 
+		needed = SCTP_DEFAULT_MAXWINDOW;
+
+	freed = 0;
+
+	if (skb_queue_empty(&asoc->base.sk->receive_queue)) {
+		freed = sctp_ulpq_renege_order(ulpq, needed);
+		if (freed < needed) {
+			freed += sctp_ulpq_renege_frags(ulpq, needed - freed);
+		}
+	}
+	/* If able to free enough room, accept this chunk. */
+	if (chunk && (freed >= needed)) {
+		__u32 tsn;
+		tsn = ntohl(chunk->subh.data_hdr->tsn);
+		sctp_tsnmap_mark(&asoc->peer.tsn_map, tsn);
+		sctp_ulpq_tail_data(ulpq, chunk, gfp);
+		
+		sctp_ulpq_partial_delivery(ulpq, chunk, gfp);
+	}
+
+	return;
+}
+
+
+
+/* Notify the application if an association is aborted and in
+ * partial delivery mode.  Send up any pending received messages.
+ */
+void sctp_ulpq_abort_pd(struct sctp_ulpq *ulpq, int gfp)
+{
+	struct sctp_ulpevent *ev = NULL;
+	struct sock *sk;
+
+	if (!ulpq->pd_mode)
+		return;
+
+	sk = ulpq->asoc->base.sk;
+	if (sctp_ulpevent_type_enabled(SCTP_PARTIAL_DELIVERY_EVENT,
+				       &sctp_sk(sk)->subscribe))
+		ev = sctp_ulpevent_make_pdapi(ulpq->asoc,
+					      SCTP_PARTIAL_DELIVERY_ABORTED,
+					      gfp);
+	if (ev)
+		__skb_queue_tail(&sk->receive_queue, sctp_event2skb(ev));
+
+	/* If there is data waiting, send it up the socket now. */
+	if (sctp_ulpq_clear_pd(ulpq) || ev)
+		sk->data_ready(sk, 0);
+}
diff -urN linux-2.4.23-pre5/net/socket.c linux-2.4.23-pre6/net/socket.c
--- linux-2.4.23-pre5/net/socket.c	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.23-pre6/net/socket.c	2003-10-01 10:38:59.000000000 -0700
@@ -325,7 +325,7 @@
  *	but we take care of internal coherence yet.
  */
 
-static int sock_map_fd(struct socket *sock)
+int sock_map_fd(struct socket *sock)
 {
 	int fd;
 	struct qstr this;
